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

问题描述 投票:-1回答:4

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

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

int main()
{
    int p=10;
    std::cout<<SQR(++p);
}
c++ macros
4个回答
5
投票

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

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

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

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

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

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

int sqr(int x)
{
    return x * x;
}

您可以考虑将其作为模板

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

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

constexpr int sqr(int x)
{
    return x * x;
}

9
投票

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

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

std::cout<<++p*++p;

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

以宏等为例

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

...

std::cout << SHIFT(1, 4);

这将导致代码

std::cout << 1 << 4;

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


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

inline int sqr(const int x)
{
    return x * x;
}

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

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


5
投票

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

std::cout<<++p*++p;

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


4
投票

因为SQR(++ p)扩展为++ p * ++ p,它在C / C ++中具有未定义的行为。在这种情况下,它在评估乘法之前递增p两次。但你不能依赖它。使用不同的C / C ++编译器可能是121(甚至42)。

© www.soinside.com 2019 - 2024. All rights reserved.