Я всегда видел примеры и случаи, когда использование макроса лучше, чем использование функции.
Может ли кто-нибудь объяснить мне на примере недостаток макроса по сравнению с функцией?
c
function
c-preprocessor
Кирол
источник
источник
Ответы:
Макросы подвержены ошибкам, потому что они полагаются на текстовую замену и не выполняют проверку типов. Например, этот макрос:
отлично работает при использовании с целым числом:
но делает очень странные вещи при использовании с выражениями:
Заключение аргументов в круглые скобки помогает, но не устраняет полностью эти проблемы.
Когда макросы содержат несколько операторов, у вас могут возникнуть проблемы с конструкциями потока управления:
Обычная стратегия для исправления этого - поместить операторы в цикл «do {...} while (0)».
Если у вас есть две структуры, которые содержат поле с одинаковым именем, но с разной семантикой, один и тот же макрос может работать с обоими, что приведет к странным результатам:
Наконец, макросы могут быть трудными для отладки, вызывая странные синтаксические ошибки или ошибки времени выполнения, которые вам необходимо раскрыть, чтобы понять (например, с помощью gcc -E), потому что отладчики не могут выполнять макросы, как в этом примере:
Встроенные функции и константы помогают избежать многих из этих проблем с макросами, но не всегда применимы. Если макросы преднамеренно используются для определения полиморфного поведения, непреднамеренного полиморфизма может быть трудно избежать. C ++ имеет ряд функций, таких как шаблоны, которые помогают создавать сложные полиморфные конструкции безопасным способом без использования макросов; подробности см. в Stroustrup's The C ++ Programming Language .
источник
x++*x++
нельзя сказать, что выражение увеличиваетсяx
дважды; на самом деле он вызывает неопределенное поведение , что означает, что компилятор может делать все, что захочет - он может увеличиватьсяx
дважды, один раз или не делать вообще; он может прерваться с ошибкой или даже заставить демонов вылететь из вашего носа .Особенности макроса :
Особенности функции :
источник
Побочные эффекты очень велики. Вот типичный случай:
расширяется до:
x
увеличивается дважды в одном и том же операторе. (и неопределенное поведение)Написание многострочных макросов также является проблемой:
Они требуют
\
в конце каждой строки.Макросы не могут ничего "вернуть", если вы не сделаете это одним выражением:
Это невозможно сделать в макросе, если вы не используете выражение GCC. (РЕДАКТИРОВАТЬ: вы можете использовать оператор запятой, хотя ... не заметили этого ... Но он все равно может быть менее читабельным.)
Порядок действий: (любезно предоставлено @ouah)
расширяется до:
Но
&
имеет более низкий приоритет, чем<
. Итак,0xFF < 42
сначала оценивается.источник
min(a & 0xFF, 42)
Пример 1:
в то время как:
Пример 2:
По сравнению с:
источник
В случае сомнений используйте функции (или встроенные функции).
Однако ответы здесь в основном объясняют проблемы с макросами, вместо того, чтобы иметь какое-то простое представление о том, что макросы являются злом, потому что возможны глупые несчастные случаи.
Вы можете знать о подводных камнях и научиться их избегать. Тогда используйте макросы только тогда, когда для этого есть веская причина.
Есть определенные исключительные случаи, когда есть преимущества использования макросов, к ним относятся:
va_args
.например: https://stackoverflow.com/a/24837037/432509 .
(
__FILE__
,__LINE__
,__func__
). проверьте наличие предварительных / пост-условий,assert
при сбое или даже статических утверждений, чтобы код не компилировался при неправильном использовании (в основном полезно для отладочных сборок).struct
членов перед преобразованием(может быть полезно для полиморфных типов) .
Или проверьте, что массив соответствует некоторому условию длины.
см .: https://stackoverflow.com/a/29926435/432509
func(FOO, "FOO");
, вы можете определить макрос, который расширяет строку для васfunc_wrapper(FOO);
(присвоение нескольким переменным для попиксельных операций - это пример того, что вы можете предпочесть макрос функции ... хотя это все еще сильно зависит от контекста, поскольку
inline
функции могут быть опцией) .По общему признанию, некоторые из них полагаются на расширения компилятора, которые не являются стандартными C. Это означает, что вы можете получить менее переносимый код или иметь
ifdef
их, поэтому они используются только тогда, когда компилятор поддерживает.Избегайте создания экземпляров с несколькими аргументами
Отмечая это, поскольку это одна из наиболее распространенных причин ошибок в макросах (
x++
например, передача , когда макрос может увеличиваться несколько раз) .можно писать макросы, которые избегают побочных эффектов при многократном создании экземпляров аргументов.
C11 Generic
Если вам нравится иметь
square
макрос, который работает с различными типами и поддерживает C11, вы можете сделать это ...Выражения оператора
Это расширение компилятора, поддерживаемое GCC, Clang, EKOPath и Intel C ++ (но не MSVC) ;
Таким образом, недостатком макросов является то, что вам нужно знать, как их использовать для начала, и что они не так широко поддерживаются.
Одно из преимуществ состоит в том, что в этом случае вы можете использовать одну и ту же
square
функцию для многих разных типов.источник
Проверка типов параметров и кода не выполняется, что может привести к раздуванию кода. Макросинтаксис также может привести к любому количеству странных крайних случаев, когда точки с запятой или порядок приоритета могут мешать. Вот ссылка, демонстрирующая некое макро- зло
источник
одним из недостатков макросов является то, что отладчики читают исходный код, который не имеет расширенных макросов, поэтому запуск отладчика в макросе не обязательно полезен. Излишне говорить, что вы не можете установить точку останова внутри макроса, как в случае с функциями.
источник
Функции выполняют проверку типов. Это дает вам дополнительный уровень безопасности.
источник
Добавляем к этому ответу ..
Макросы подставляются непосредственно в программу препроцессором (поскольку они в основном являются директивами препроцессора). Таким образом, они неизбежно используют больше памяти, чем соответствующая функция. С другой стороны, функции требуется больше времени для вызова и возврата результатов, и этих накладных расходов можно избежать, используя макросы.
Также у макросов есть некоторые специальные инструменты, которые могут помочь с переносимостью программы на разные платформы.
Макросам не нужно назначать тип данных для их аргументов, в отличие от функций.
В целом они являются полезным инструментом в программировании. И как макроинструкции, так и функции могут использоваться в зависимости от обстоятельств.
источник
В приведенных выше ответах я не заметил одного преимущества функций над макросами, которое, на мой взгляд, очень важно:
Функции можно передавать как аргументы, макросы - нет.
Конкретный пример: вы хотите написать альтернативную версию стандартной функции strpbrk, которая будет принимать вместо явного списка символов для поиска в другой строке функцию (указатель на), которая будет возвращать 0, пока символ не будет обнаружено, что он проходит некоторый тест (определенный пользователем). Одна из причин, по которой вы можете захотеть это сделать, заключается в том, чтобы вы могли использовать другие стандартные библиотечные функции: вместо предоставления явной строки, полной знаков препинания, вы могли бы вместо этого передать ctype.h 'ispunct' и т. Д. Если 'ispunct' был реализован только как макрос, это не сработает.
Есть много других примеров. Например, если ваше сравнение выполняется с помощью макроса, а не функции, вы не можете передать его в stdlib.h 'qsort'.
Аналогичная ситуация в Python - «печать» в версии 2 по сравнению с версией 3 (непроходимый оператор или допустимая функция).
источник
Если вы передадите функцию в качестве аргумента макросу, она будет вычисляться каждый раз. Например, если вы вызываете один из самых популярных макросов:
как это
functionThatTakeLongTime будет оцениваться 5 раз, что может значительно снизить производительность
источник