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

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

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

вместо inline-функции. Пожалуйста, извините вопрос новичка, но что такого плохого в макросах в этом случае?

8 ответов


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

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

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

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

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

#define radian2degree(a) (a * 57.295779513082)

должен быть

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

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


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

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

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

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

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


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

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;

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

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

редактировать

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


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

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

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

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

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


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

defines можно переопределить неопределенным, и нет проверки типа.


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


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


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

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