#define SQR(x)x*x.意外的答案

Inq*_*ive -1 c++ macros

为什么这个宏给出输出144而不是121?

#include<iostream>
#define SQR(x) x*x

int main()
{
    int p=10;
    std::cout<<SQR(++p);
}
Run Code Online (Sandbox Code Playgroud)

Som*_*ude 9

这是预处理器宏的缺陷.问题是表达式++p被使用了两次,因为预处理器只是简单地逐字替换宏的"调用".

那么编译器在宏扩展后看到了什么

std::cout<<++p*++p;
Run Code Online (Sandbox Code Playgroud)

根据宏的不同,如果不小心将括号放在需要的位置,也可能会出现运算符优先级问题.

以宏等为例

// Macro to shift `a` by `b` bits
#define SHIFT(a, b)  a << b

...

std::cout << SHIFT(1, 4);
Run Code Online (Sandbox Code Playgroud)

这将导致代码

std::cout << 1 << 4;
Run Code Online (Sandbox Code Playgroud)

这可能不是想要或预期的.


如果您想避免这种情况,请改用内联函数:

inline int sqr(const int x)
{
    return x * x;
}
Run Code Online (Sandbox Code Playgroud)

这有两件事情要做:首先是表达式++p只会被评估一次.另一件事是,现在你不能将除int值之外的任何东西传递给函数.使用预处理器宏,您可以"调用"它SQR("A"),而预处理器在您从编译器获取(有时)隐含错误时并不在意.

此外,在被标记时inline,编译器可以完全跳过实际的函数调用并将(正确的)x*x直接放在调用的位置,从而使其像宏扩展一样"优化".


ere*_*der 5

因为您使用的是未定义的行为.扩展宏时,您的代码变为:

std::cout<<++p*++p;
Run Code Online (Sandbox Code Playgroud)

在同一语句中多次使用相同变量的递增/递减运算符是未定义的行为.


ste*_*fan 5

使用此宏进行平方的方法有两个问题:

首先,对于参数++p,增量操作执行两次.这当然不是故意的.(作为一般经验法则,不要在"一行"中做几件事.将它们分成更多的陈述.).它甚至没有停止递增两次:这些增量的顺序没有定义,因此没有保证此操作的结果!

其次,即使你没有++p参数,你的宏中仍然存在一个错误!考虑输入1 + 1.预期的产出是4.1+1没有副作用,所以它应该没问题,不应该吗?不,因为SQR(1 + 1)转换为1 + 1 * 1 + 1评估结果3.

要至少部分修复此宏,请使用括号:

#define SQR(x) (x) * (x)
Run Code Online (Sandbox Code Playgroud)

总之,你应该简单地用一个函数替换它(添加类型安全!)

int sqr(int x)
{
    return x * x;
}
Run Code Online (Sandbox Code Playgroud)

您可以考虑将其作为模板

template <typename Type>
Type sqr(Type x)
{
   return x * x; // will only work on types for which there is the * operator.
}
Run Code Online (Sandbox Code Playgroud)

并且你可以添加一个constexpr(C++ 11),如果你打算在模板中使用正方形,这是很有用的:

constexpr int sqr(int x)
{
    return x * x;
}
Run Code Online (Sandbox Code Playgroud)