如何正确扩展此宏?

时间:2012-09-20 04:27:39

标签: c++ c macros

当我定义这个宏时:

#define SQR(x) x*x

让我们说这个表达式:

SQR(a+b)

此表达式将被宏替换,如下所示:

a+b*a+b

但是,如果我在表达式之前放置一个++运算符:

++SQR(a+b)

现在的表情是什么样的?这个++是否适用于SQR参数的每个部分?像这样:

++a+b*++a+b

这里我给出一个简单的程序:

#define SQR(x) x*x
int a, k = 3;
a = SQR(k+1) // 7
a = ++SQR(k+1) //9

5 个答案:

答案 0 :(得分:5)

在定义宏时,你基本上总是想要将宏参数放在parens中以防止你的第一个例子中出现那种奇怪的行为,并将结果放在parens中以便安全使用副作用。使用

#define SQR(x) ((x)*(x))

SQR(a+b)扩展为((a+b)*(a+b)),这在数学上是正确的(与a+b*a+b不同,等于ab + a + b)。

在宏之前或之后放置内容不会进入宏。因此,++SQR(x)在您的示例中变为++x*x

请注意以下事项:

int a=3, b=1;
SQR(a+b) // ==> a+b*a+b = 3+1*3+1 = 7
++SQR(a+b) // ==> ++a+b*a+b ==> 4 + 1*4 + 1 = 9
           // since preincrement will affect the value of a before it is read.

你看到++SQR(a+b)似乎增加了2,因为preincrement在a之前开始读取时间,即a增量,然后使用两次,因此结果高于预期值2。

注意正如@JonathanLeffler指出的那样,后一个调用会调用未定义的行为;评估不保证从左到右进行。它可能会在不同的编译器/操作系统上产生不同的结果,因此永远不应该依赖它。

答案 1 :(得分:1)

对于C ++,定义此宏的正确方法是不使用宏,而是使用:

template<typename T> static T SQR( T a ) { return a*a; }

这样可以解决一些宏观错误的可怕情况:

例如:

SQR(++a); 
函数形式++a

将被评估一次。在宏表单中,当您在序列点之间多次修改和读取值时,您会得到未定义的行为(至少对于C ++而言)

答案 2 :(得分:0)

宏定义只是替换代码,因此通常最好放入括号,否则代码可能会以您不想要的方式替换。

因此,如果您将其定义为:

#define SQR(x) ((x)*(x))

然后

  

++ SQR(a + b)= ++((a + b)*(a + b))

答案 3 :(得分:0)

在您的示例中,++SQR(a+b)应扩展为++a+b*a+b 因此,如果a == 3且b == 1,如果编译器从左到右对其进行评估,您将得到答案9

但您的陈述++SQR(3+1)不正确,因为它会被++3+1*3+1扩展为++3无效。

答案 4 :(得分:0)

在预处理器中,它的计算结果为++ a + b * a + b。正确的方法是在每个术语周围放置括号并围绕整个事项,例如:

#define SQR(x)  ((x)*(x))