宏C/C++的奇怪行为

Strange behaviour of macros C/C++

本文关键字:C++      更新时间:2023-10-16

我正在使用一些宏,并观察到一些奇怪的行为。

我将PI定义为一个常数,然后在宏中使用它将度数转换为弧度,将弧度转换为度数。度到弧度很好,但弧度到度不行:

piTest.cpp:

#include <cmath>
#include <iostream>
using namespace std;
#define PI atan(1) * 4
#define radians(deg)  deg * PI / 180
#define degrees(rad)  rad * 180 / PI
int main()
{
  cout << "pi: " << PI << endl;
  cout << "PI, in degrees: " << degrees(PI) << endl;
  cout << "45 degrees, in rad: " << radians(45) << endl;
  cout << "PI * 180 / PI: " << (PI * 180 / PI) << endl;
  cout << "3.14159 * 180 / 3.14159: " << (3.14159 * 180 / 3.14159) << endl;
  cout << "PI * 180 / 3.14159: " << (PI * 180 / 3.14159) << endl;
  cout << "3.14159 * 180 / PI: " << (3.14159 * 180 / PI) << endl;
  return 0;
}

当我编译和运行时,我得到以下输出:

pi: 3.14159
PI, in degrees: 2880
45 degrees, in rad: 0.785398
PI * 180 / PI: 2880
3.14159 * 180 / 3.14159: 180
PI * 180 / 3.14159: 180
3.14159 * 180 / PI: 2880

看起来我的常数PI在分子中起作用,但在分母中不起作用。我在C中观察到了同样的行为。我运行的是gcc版本4.6.3

有人能解释我为什么会有这种行为吗?

宏是(相对简单的)文本替换。

在定义中使用括号(同时包含宏本身和宏参数):

#define PI (atan(1) * 4)
#define radians(deg)  ((deg) * PI / 180)
#define degrees(rad)  ((rad) * 180 / PI)

首先,cmath定义了M_PI,使用它。

其次,cpp宏进行文本替换。这意味着:

#define PI atan(1) * 4
a = 1 / PI;

将变成这样:

a = 1 / atan(1) * 4;

在c/c++编译器有机会看到你的代码之前,它会把它等效为:

a = (1 / atan(1)) * 4;

这不是你想要的。

你的定义应该是这样的:

#define PI (atan(1) * 4)

一切都会好起来的。

这并不是真正奇怪的行为,而是c预处理器的行为。

你应该在网上搜索宏的其他陷阱。(提示:参数传递)

您应该为宏使用括号来指定优先级。除此之外,我认为在许多情况下,math.h将为您定义

的PI

宏只是在不考虑上下文的情况下进行文本替换,所以最终得到的是:

cout << "PI, in degrees: " << atan(1) * 4 * 180 / atan(1) * 4 << endl;

请注意,第二个atan(1) * 4周围明显缺少parens,导致它只除以atan(1),然后乘以4。

相反,使用内联函数和全局变量:

const double PI = atan(1) * 4;
double radians(double deg) { return deg * PI / 180; }
double degrees(double rad) { return rad * 180 / PI; }

还有一个好的做法:在所有参数周围加括号:

#define radians(deg)  ((deg) * PI / 180)

因为作为参数传递的表达式可能也包含运算符。

或者更好:使用(内联)函数而不是宏,以避免在多次评估参数时产生意外的副作用,如以下所示:

#define sqr(x)  ((x) * (x))

内联函数的唯一缺点是:只能为一种类型定义它们(除非使用C++模板)

相关文章:
  • 没有找到相关文章