gcc — Странное поведение макросов C / Stack Overflow

Я использую некоторые макросы и наблюдаю за некоторым странным поведением.

Я определил 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.

Кто-нибудь может объяснить, почему у меня такое поведение?

3

Решение

Макросы — это (относительно простые) текстовые замены.

Используйте скобки в своих определениях (как для включения самого макроса, так и аргументов макроса):

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

Другие решения

Первый, 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-препроцессора.

Вы должны искать в Интернете другие подводные камни с макросами. (подсказка: передача параметров)

5

Вы должны использовать скобки для макросов, чтобы указать приоритет. В дополнение к этому, я думаю, что во многих случаях math.h определит для вас PI

2

Макросы просто выполняют подстановку текста без учета контекста, поэтому вы получите:

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

Обратите внимание на явное отсутствие паренов вокруг второго atan(1) * 4в результате чего делится только на 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; }
2

Также хорошая практика: добавьте скобки вокруг всех ваших параметров:

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

потому что выражение, которое вы передаете в качестве параметра, может также включать операторы.

Или даже лучше: используйте (inline-) функции вместо макросов, чтобы избежать неожиданностей с побочными эффектами, когда параметр оценивается несколько раз, как здесь:

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

Единственный недостаток, который вы получаете с встроенными функциями: вы можете определить их только для одного типа (если вы не используете шаблоны C ++)

1
По вопросам рекламы [email protected]