Когда вы должны использовать макросы вместо встроенных функций?

В предыдущем question то, что я думал, было хорошим ответом, было отклонено за предлагаемое использование макросов

#define radian2degree(a) (a * 57.295779513082)
#define degree2radian(a) (a * 0.017453292519)

вместо встроенных функций. Извините вопрос новичка, но что в этом случае так плохо для макросов?

Ответ 1

Там есть пара строго злых вещей о макросах.

Это текстовая обработка и не ограничена. Если вы #define foo 1, то любое последующее использование foo в качестве идентификатора не будет выполнено. Это может привести к ошибкам нечетной компиляции и труднодоступным ошибкам времени выполнения.

Они не принимают аргументы в нормальном смысле. Вы можете написать функцию, которая будет принимать два значения int и вернуть максимум, потому что аргументы будут оцениваться один раз и значения, используемые после этого. Вы не можете написать макрос, чтобы сделать это, потому что он будет оценивать по крайней мере один аргумент дважды и сбой с чем-то вроде max(x++, --y).

Там также распространены подводные камни. Трудно получить в них несколько утверждений, и они требуют много, возможно, лишних круглых скобок.

В вашем случае вам нужны скобки:

#define radian2degree(a) (a * 57.295779513082)

должно быть

#define radian2degree(a) ((a) * 57.295779513082)

и вы все еще наступаете на любого, кто пишет функцию radian2degree в какой-то внутренней области, уверен, что это определение будет работать в своей области.

Ответ 2

Большинство других ответов обсуждают, почему макросы являются злыми, в том числе, как ваш пример имеет общий недостаток использования макросов. Здесь Stroustrup возьмет: http://www.research.att.com/~bs/bs_faq2.html#macro

Но ваш вопрос спрашивал, какие макросы все еще хороши. Есть некоторые вещи, где макросы лучше, чем встроенные функции, и что там, где вы делаете то, что просто невозможно сделать с помощью встроенных функций, таких как:

  • вставка темени
  • относящихся к номерам строк или таковым (например, для создания сообщений об ошибках в assert())
  • работа с вещами, которые не являются выражениями (например, сколько реализаций offsetof() используют использование имени типа для создания операции трансляции)
  • макрос, чтобы получить количество элементов массива (не может сделать это с помощью функции, так как имя массива слишком легко сбрасывается на указатель)
  • создание типа "полиморфных" функций, подобных в C, где шаблоны недоступны

Но с языком, который имеет встроенные функции, более частое использование макросов не должно быть необходимым. Я даже не хочу использовать макросы, когда имею дело с компилятором C, который не поддерживает встроенные функции. И я стараюсь не использовать их для создания агностических функций типа, если это вообще возможно (создание нескольких функций с индикатором типа как части имени).

Я также перешел к использованию перечислений для именованных числовых констант вместо #define.

Ответ 3

Для этого конкретного макроса, если я использую его следующим образом:

int x=1;
x = radian2degree(x);
float y=1;
y = radian2degree(y);

не будет проверки типа, а x, y будет содержать разные значения.

Кроме того, следующий код

float x=1, y=2;
float z = radian2degree(x+y);

не будет делать то, что вы думаете, так как оно будет переведено на

float z = x+y*0.017453292519;

вместо

float z = (x+y)+0.017453292519;

который является ожидаемым результатом.

Это всего лишь несколько примеров неправильного поведения и неправильного использования макросов.

Edit

вы можете увидеть дополнительные обсуждения об этом здесь

Ответ 4

Макросы относительно часто подвергаются насилию, и их можно легко ошибочно использовать, как показано в вашем примере. Возьмем выражение radian2degree (1 + 1):

  • с макросом он будет расширяться до 1 + 1 * 57.29... = 58.29...
  • с функцией будет то, что вы хотите, а именно (1 + 1) * 57.29... =...

В более общем плане макросы являются злыми, потому что они выглядят как функции, поэтому они обманывают вас, используя их, как функции , но у них есть тонкие правила для собственных. В этом случае правильным способом было бы записать его (обратите внимание на paranthesis вокруг a):

#define radian2degree(a) ((a) * 57.295779513082)

Но вы должны придерживаться встроенных функций. См. Эти ссылки из С++ FAQ Lite для получения дополнительных примеров злых макросов и их тонкостей:

Ответ 5

если возможно, всегда используйте встроенную функцию. Они являются типичными и не могут быть легко переопределены.

определяет, может быть переопределено undefined, и проверка типа не выполняется.

Ответ 6

Препроцессор компилятора - это финишная вещь, и, следовательно, ужасный кандидат на умные трюки. Как указывали другие, легко компилятору неправильно понять ваше намерение с помощью макроса, и вам легко понять, что на самом деле делает макрос, но, самое главное, вы не можете входить в макросы в отладчике!

Ответ 7

Макросы являются злыми, потому что в конечном итоге вы можете передать больше, чем переменную или скаляр, и это может разрешиться при нежелательном поведении (определите макс-макрос, чтобы определить max между a и b, но передайте a ++ и b ++ для макроса и посмотрите, что произойдет).

Ответ 8

Если ваша функция все равно будет встроена, разница между функцией и макросом отсутствует. Тем не менее, существует несколько различий в удобстве использования функции и макроса, все из которых способствуют использованию функции.

Если вы построите макрос правильно, проблем нет. Но если вы используете функцию, компилятор будет делать это правильно для вас каждый раз. Поэтому использование функции затрудняет запись плохого кода.