【问题标题】:#define SQR(x) x*x. Unexpected Answer#define SQR(x) x*x。意外的答案
【发布时间】:2014-02-17 15:54:41
【问题描述】:

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

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

int main()
{
    int p=10;
    std::cout<<SQR(++p);
}

【问题讨论】:

  • 如果你想学习 C++,为什么不使用内联函数呢?如您所见,宏可能会导致评估问题...
  • 见 Bjarne Stroustrups FAQ。它解释了使用宏有什么问题,并给出了您遇到问题的完全相同的示例。

标签: c++ macros


【解决方案1】:

这是预处理器宏的一个缺陷。问题在于表达式++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 直接放在调用位置,从而使其与宏扩展一样“优化”。

【讨论】:

  • 您可以将'A' 或任何其他数值传递给此函数(假设您打算使用int 的参数类型);更糟糕的是,如果你传递一个浮点值,它会默默地给出一个意想不到的结果。模板可能更合适,尤其是作为宏的替代品。
  • @MikeSeymour 当然你是对的,但我认为模板目前可能有点超出 OP 的范畴。
【解决方案2】:

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

首先,对于参数++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;
}

【讨论】:

    【解决方案3】:

    因为您使用的是未定义的行为。当宏展开时,你的代码变成这样:

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

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

    【讨论】:

    • 你有这个参考吗?如何/为什么这是未定义的?谢谢。
    【解决方案4】:

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

    【讨论】:

    • 而且,更重要的是,它在 C++ 中也有未定义的行为(关于这个问题)。
    • @Angew:是的,当然。谢谢你。我在文本中将 C 更改为 C/C++。
    猜你喜欢
    • 1970-01-01
    • 2020-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-09
    相关资源
    最近更新 更多