我正在使用一些宏,并观察到一些奇怪的行为。
我已将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
任何人都可以解释为什么我会出现这种行为吗?
答案 0 :(得分:9)
宏是(相对简单的)文本替换。
在定义中使用括号(包括宏本身和宏参数):
#define PI (atan(1) * 4)
#define radians(deg) ((deg) * PI / 180)
#define degrees(rad) ((rad) * 180 / PI)
答案 1 :(得分:5)
首先,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-preprocessor的行为记录良好。
您应该在网上搜索有关宏的其他陷阱。 (提示:参数传递)
答案 2 :(得分:2)
您应该使用宏的括号来指定优先级。除此之外,我认为在很多情况下math.h会为你定义PI
答案 3 :(得分:2)
宏只是在不考虑上下文的情况下进行文本替换,所以你最终得到的是:
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; }
答案 4 :(得分:1)
同样好的做法:在所有参数周围添加括号:
#define radians(deg) ((deg) * PI / 180)
因为您作为参数传递的表达式也可能包含运算符。
甚至更好:使用(内联)函数而不是宏,以避免在多次评估参数时出现副作用的意外情况,如下所示:
#define sqr(x) ((x) * (x))
内联函数的唯一缺点是:您只能为一种类型定义它们(除非您使用C ++模板)