Strange behaviour of macros C/C++

2019-01-12 11:20发布

问题:

I'm using some macros, and observing some strange behaviour.

I've defined PI as a constant, and then used it in macros to convert degrees to radians and radians to degrees. Degrees to radians works fine, but radians to degrees does not:

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;

}

When I compile and run, I get the following output:

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

It seems like my constant PI works in the numerator, but not the denominator. I've observed the same behaviour in C. I'm running gcc version 4.6.3

Can anyone explain why I'm getting this behaviour?

回答1:

Macros are (relatively simple) textual substitutions.

Use parentheses in your definitions (both to enclose the macro itself and the macro arguments):

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


回答2:

First, cmath defines M_PI, use that.

Second, cpp macros do textual substitution. Which mean that this:

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

will be turned into this:

a = 1 / atan(1) * 4;

before the c/c++ compiler gets a chance to see your code, and it will treat it equivalent to this:

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

which is not what you want.

Your define should look like this:

#define PI (atan(1) * 4)

and everything should be fine.

This is not really strange behaviour, but well documented behaviour of the c-preprocessor.

You should search the web for other pitfalls with macros. (hint: parameter passing)



回答3:

You should use parenthesis for your macros to specify precedence. In addition to that, i think in many cases math.h will define PI for you



回答4:

Macros just do text substitution without regard for context, so what you wind up with is:

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

Note the distinct lack of parens around the second atan(1) * 4, causing it to divide only by atan(1) and then multiply by 4.

Instead, use inline functions and globals:

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


回答5:

Also good practice: Add brackets around all your parameters:

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

because the expression you pass as parameter might include operators, too.

Or even better: Use (inline-) functions instead of macros, to avoid surprises with sideeffects when a parameter is evaluated multiple times like here:

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

The only disadvantage you get with inline functions: You can define them for one type, only (unless you use C++ templates)