Макросы препроцессора - это просто шаблоны подстановки, применяемые к вашему коду. Их можно использовать практически в любом месте вашего кода, потому что они заменяются своими расширениями до начала любой компиляции.
Встроенные функции - это фактические функции, тело которых напрямую вводится в их место вызова. Их можно использовать только там, где уместен вызов функции.
Теперь, что касается использования макросов и встроенных функций в функционально-подобном контексте, имейте в виду, что:
Макросы небезопасны по типу и могут быть расширены независимо от того, являются ли они синтаксически правильными - на этапе компиляции будут сообщаться об ошибках, вызванных проблемами расширения макроса.
Макросы можно использовать там, где вы не ожидаете, что приведет к проблемам
Макросы более гибкие, поскольку они могут расширять другие макросы, в то время как встроенные функции не обязательно это делают.
Макросы могут вызывать побочные эффекты из-за их расширения, поскольку входные выражения копируются везде, где они появляются в шаблоне.
Встроенная функция не всегда гарантированно встроена - некоторые компиляторы делают это только в сборках выпуска или когда они специально настроены для этого. Кроме того, в некоторых случаях встраивание может быть невозможно.
Встроенные функции могут предоставлять область видимости для переменных (особенно статических), макросы препроцессора могут делать это только в блоках кода {...}, а статические переменные не будут вести себя точно так же.
Не всегда гарантируется, что встроенная функция будет встроенной: потому что компилятор не будет встроенным, если это будет генерировать более медленный код и т. Д. Компилятор выполняет большой анализ, который не может выполнить инженер, и делает правильные вещи.
Мартин Йорк
14
Я считаю, что рекурсивные функции - еще один пример, когда большинство компиляторов игнорируют встраивание.
LBushkin
Есть ли в этом случае какие-либо важные отличия в C от C ++?
rzetterberg
7
Один момент, о котором не упоминается, заключается в том, что на встраивание могут влиять флаги компиляции. Например, когда вы строите для максимальной скорости (например, GCC -O2 / -O3), компилятор выберет встраивание многих функций, но когда вы строите для минимального размера (-Os), если обычно встроенные функции вызываются только один раз (или очень маленькие функции ). С макросами такого выбора нет.
dbrank0
Макросы не могут охватывать спецификатор доступа (например, частный или защищенный), пока возможны встроенные функции.
Hit's
78
Во-первых, макросы препроцессора просто копируют и вставляют в код перед компиляцией. Таким образом, нет проверки типа , и могут появиться некоторые побочные эффекты
Например, если вы хотите сравнить 2 значения:
#define max(a,b)((a<b)?b:a)
Побочные эффекты появляются, если вы используете, max(a++,b++)например ( aили bбудут увеличиваться вдвое). Вместо этого используйте (например)
Просто хочу добавить к вашему примеру, что помимо побочного эффекта, макрос также может вводить дополнительную рабочую нагрузку, max(fibonacci(100), factorial(10000))
учтите
Все говорят о проверке типов, но только вы привели реальный пример, поэтому я поддерживаю этот ответ.
Иванзиньо
16
Встроенная функция расширяется компилятором, а макросы расширяются препроцессором, что является простой текстовой заменой.
При вызове макроса проверка типа не выполняется, в то время как проверка типа выполняется во время вызова функции.
Во время расширения макроса могут возникнуть нежелательные результаты и неэффективность из-за переоценки аргументов и порядка операций. Например
#define MAX(a,b)((a)>(b)?(a):(b))int i =5, j = MAX(i++,0);
приведет к
int i =5, j =((i++)>(0)?(i++):(0));
Аргументы макроса не оцениваются перед раскрытием макроса
#define MUL(a, b) a*bint main(){// The macro is expended as 2 + 3 * 3 + 5, not as 5*8
printf("%d", MUL(2+3,3+5));return0;}// Output: 16`
Ключевое слово return нельзя использовать в макросах для возврата значений, как в случае функций.
Встроенные функции могут быть перегружены
Токены, передаваемые в макросы, могут быть объединены с помощью оператора ##, называемого оператором вставки токена.
Макросы обычно используются для повторного использования кода, а встроенные функции используются для устранения временных затрат (лишнего времени) во время вызова функции (избегая перехода к подпрограмме).
Ключевое отличие - проверка типов. Компилятор проверит, имеет ли то, что вы передаете в качестве входных значений, типы, которые могут быть переданы в функцию. Это не так с макросами препроцессора - они раскрываются перед любой проверкой типов, и это может вызвать серьезные и трудно поддающиеся обнаружению ошибки.
встроенные функции похожи на макросы (потому что код функции раскрывается в момент вызова во время компиляции), встроенные функции анализируются компилятором, тогда как макросы расширяются препроцессором. В результате есть несколько важных отличий:
Встроенные функции следуют всем протоколам безопасности типов, применяемым к обычным функциям.
Встроенные функции указываются с использованием того же синтаксиса, что и любые другие функции, за исключением того, что они включают ключевое слово inline в объявление функции.
Выражения, переданные в качестве аргументов встроенным функциям, оцениваются один раз.
Встроенная функция будет поддерживать семантику значения, тогда как макрос препроцессора просто копирует синтаксис. Вы можете получить очень тонкие ошибки с макросом препроцессора, если вы используете аргумент несколько раз - например, если аргумент содержит мутацию типа «i ++», выполнение этого дважды является большим сюрпризом. У встроенной функции этой проблемы не будет.
Встроенная функция синтаксически ведет себя так же, как обычная функция, обеспечивая безопасность типов и область действия для локальных переменных функции и доступ к членам класса, если это метод. Также при вызове встроенных методов вы должны придерживаться частных / защищенных ограничений.
Чтобы понять разницу между макросом и встроенной функцией , во-первых, мы должны знать, что именно они собой представляют и когда мы должны их использовать.
ФУНКЦИИ :
intSquare(int x){return(x*X);}int main(){intvalue=5;int result =Square(value);
cout << result << endl;}
Вызовы функций связаны с накладными расходами, поскольку после завершения выполнения функции она должна знать, куда она должна вернуться, а также должна сохранить значение в памяти стека.
Для небольших приложений это не будет проблемой, но давайте возьмем пример финансовых приложений, в которых каждую секунду происходят тысячи транзакций, мы не можем использовать вызовы функций.
MACROS:
# define Square(x) x*x;int main(){intvalue=5;int result =Square(value);
cout << result << endl;}
Макросы работают на этапе предварительной обработки, т.е. на этом этапе операторы, написанные с ключевым словом #, будут заменены содержимым, т.е.
int результат = Квадрат (x * x)
Но у макросов есть ошибки, связанные с этим.
#defineSquare(x) x*x
int main(){int val =5;int result =Square(val +1);
cout << result << endl;return0;}
Здесь результат 11, а не 36 .
ВСТРОЕННЫЕ ФУНКЦИИ :
inlineintSquare(int x){return x * x;}int main(){usingnamespace std;int val =5;int result =Square(val +1);
cout << result << endl;return0;}
Результат 36
Ключевое слово Inline запрашивает компилятор заменить вызов функции телом функции, здесь вывод правильный, потому что сначала вычисляется выражение, а затем передается. Это уменьшает накладные расходы на вызов функции, так как нет необходимости сохранять адрес возврата и стек. Для аргументов функции память не требуется.
Сравнение макросов и встроенных функций:
Макросы работают через подстановку, тогда как во встроенных функциях вызов функции заменяется телом.
Макросы подвержены ошибкам из-за подстановки, в то время как встроенные функции безопасны в использовании.
У макросов нет адреса, тогда как у встроенных функций есть адрес.
Макросы сложно использовать с несколькими строками кода, а встроенные функции - нет.
В C ++ макросы нельзя использовать с функциями-членами, тогда как встроенные функции могут быть.
ВЫВОД:
Встроенные функции иногда более полезны, чем макросы, поскольку они повышают производительность и безопасны в использовании, а также сокращают накладные расходы на вызов функций. Это просто запрос к компилятору, некоторые функции не будут встроены, например:
большие функции
функции, имеющие слишком много условных аргументов
рекурсивный код и код с циклами и т. д.
что хорошо, потому что это всякий раз, когда компилятор считает, что лучше всего поступить по-другому.
Просто замечание: макрос может быть исправлен так, чтобы оценивать то же самое число в скобках. Однако это по-прежнему подвержено ошибкам, так как вам нужно подумать об абсолютной глупой замене и обо всех случаях во время реализации.
Майк
0
В GCC (насчет других я не уверен) объявление функции встроенной - это просто подсказка для компилятора. В конце концов, компилятор должен решить, будет ли он включать тело функции при каждом ее вызове.
Разница между встроенными функциями и макросами препроцессора относительно велика. Макросы препроцессора - это просто замена текста в конце концов. Вы отказываетесь от многих возможностей компилятора выполнять проверку проверки типов аргументов и возвращаемого типа. Оценка аргументов сильно отличается (если выражения, которые вы передаете в функции, имеют побочные эффекты, вам будет очень интересно провести время за отладкой). Существуют тонкие различия в том, где можно использовать функции и макросы. Например, если бы у меня было:
#define MACRO_FUNC(X)...
Где MACRO_FUNC, очевидно, определяет тело функции. Следует проявлять особую осторожность, чтобы она работала правильно во всех случаях, когда функция может быть использована, например, плохо написанный MACRO_FUNC вызовет ошибку в
if(MACRO_FUNC(y)){...body
}
Там можно было без проблем использовать обычную функцию.
С точки зрения кодирования встроенная функция похожа на функцию. Таким образом, различия между встроенной функцией и макросом такие же, как и различия между функцией и макросом.
С точки зрения компиляции встроенная функция похожа на макрос. Он вводится непосредственно в код, а не вызывается.
В общем, вы должны рассматривать встроенные функции как обычные функции с добавлением некоторой незначительной оптимизации. И, как и большинство оптимизаций, компилятор должен решить, действительно ли он хочет применять его. Часто компилятор с радостью игнорирует любые попытки программиста встроить функцию по разным причинам.
встроенные функции будут вести себя как вызов функции, если в ней существует какой-либо итеративный или рекурсивный оператор, чтобы предотвратить повторное выполнение инструкций. Это очень полезно для экономии общей памяти вашей программы.
#include<iostream>usingnamespace std;#define NUMBER 10//macros are preprocessed while functions are not.int number(){return10;}/*In macros, no type checking(incompatible operand, etc.) is done and thus use of micros can lead to errors/side-effects in some cases.
However, this is not the case with functions.
Also, macros do not check for compilation error (if any). Consider:- */#define CUBE(b) b*b*b
int cube(int a){return a*a*a;}int main(){
cout<<NUMBER<<endl<<number()<<endl;
cout<<CUBE(1+3);//Unexpected output 10
cout<<endl<<cube(1+3);// As expected 64return0;}
Макросы обычно быстрее функций, поскольку они не связаны с фактическими накладными расходами на вызов функций.
Некоторые недостатки макросов: отсутствует проверка типов; трудно отлаживать, поскольку они вызывают простую замену; макрос не имеет пространства имен, поэтому макрос в одном разделе кода может повлиять на другой раздел. Макросы могут вызывать побочные эффекты, как показано в примере CUBE () выше.
Макросы обычно представляют собой один лайнер. Однако они могут состоять более чем из одной строки, в функциях таких ограничений нет.
Ответы:
Макросы препроцессора - это просто шаблоны подстановки, применяемые к вашему коду. Их можно использовать практически в любом месте вашего кода, потому что они заменяются своими расширениями до начала любой компиляции.
Встроенные функции - это фактические функции, тело которых напрямую вводится в их место вызова. Их можно использовать только там, где уместен вызов функции.
Теперь, что касается использования макросов и встроенных функций в функционально-подобном контексте, имейте в виду, что:
источник
Во-первых, макросы препроцессора просто копируют и вставляют в код перед компиляцией. Таким образом, нет проверки типа , и могут появиться некоторые побочные эффекты
Например, если вы хотите сравнить 2 значения:
Побочные эффекты появляются, если вы используете,
max(a++,b++)
например (a
илиb
будут увеличиваться вдвое). Вместо этого используйте (например)источник
max(fibonacci(100), factorial(10000))
Встроенная функция расширяется компилятором, а макросы расширяются препроцессором, что является простой текстовой заменой.
При вызове макроса проверка типа не выполняется, в то время как проверка типа выполняется во время вызова функции.
Во время расширения макроса могут возникнуть нежелательные результаты и неэффективность из-за переоценки аргументов и порядка операций. Например
приведет к
Аргументы макроса не оцениваются перед раскрытием макроса
Ключевое слово return нельзя использовать в макросах для возврата значений, как в случае функций.
Встроенные функции могут быть перегружены
Токены, передаваемые в макросы, могут быть объединены с помощью оператора ##, называемого оператором вставки токена.
Макросы обычно используются для повторного использования кода, а встроенные функции используются для устранения временных затрат (лишнего времени) во время вызова функции (избегая перехода к подпрограмме).
источник
Ключевое отличие - проверка типов. Компилятор проверит, имеет ли то, что вы передаете в качестве входных значений, типы, которые могут быть переданы в функцию. Это не так с макросами препроцессора - они раскрываются перед любой проверкой типов, и это может вызвать серьезные и трудно поддающиеся обнаружению ошибки.
Вот несколько других, менее очевидных моментов.
источник
Чтобы добавить еще одно отличие к уже приведенным: вы не можете пройти через
#define
отладчик, но можете пройти через встроенную функцию.источник
Макросы игнорируют пространства имен. И это делает их злыми.
источник
встроенные функции похожи на макросы (потому что код функции раскрывается в момент вызова во время компиляции), встроенные функции анализируются компилятором, тогда как макросы расширяются препроцессором. В результате есть несколько важных отличий:
В некоторых случаях выражения, переданные в качестве аргументов макросу, можно вычислить более одного раза. http://msdn.microsoft.com/en-us/library/bf6bf4cf.aspx
макросы раскрываются во время предварительной компиляции, вы не можете использовать их для отладки, но вы можете использовать встроенные функции.
- хорошая статья : http://www.codeguru.com/forum/showpost.php?p=1093923&postcount=1
;
источник
Встроенная функция будет поддерживать семантику значения, тогда как макрос препроцессора просто копирует синтаксис. Вы можете получить очень тонкие ошибки с макросом препроцессора, если вы используете аргумент несколько раз - например, если аргумент содержит мутацию типа «i ++», выполнение этого дважды является большим сюрпризом. У встроенной функции этой проблемы не будет.
источник
Встроенная функция синтаксически ведет себя так же, как обычная функция, обеспечивая безопасность типов и область действия для локальных переменных функции и доступ к членам класса, если это метод. Также при вызове встроенных методов вы должны придерживаться частных / защищенных ограничений.
источник
Чтобы понять разницу между макросом и встроенной функцией , во-первых, мы должны знать, что именно они собой представляют и когда мы должны их использовать.
ФУНКЦИИ :
Вызовы функций связаны с накладными расходами, поскольку после завершения выполнения функции она должна знать, куда она должна вернуться, а также должна сохранить значение в памяти стека.
Для небольших приложений это не будет проблемой, но давайте возьмем пример финансовых приложений, в которых каждую секунду происходят тысячи транзакций, мы не можем использовать вызовы функций.
MACROS:
Но у макросов есть ошибки, связанные с этим.
Здесь результат 11, а не 36 .
ВСТРОЕННЫЕ ФУНКЦИИ :
Результат 36
Ключевое слово Inline запрашивает компилятор заменить вызов функции телом функции, здесь вывод правильный, потому что сначала вычисляется выражение, а затем передается. Это уменьшает накладные расходы на вызов функции, так как нет необходимости сохранять адрес возврата и стек. Для аргументов функции память не требуется.
Сравнение макросов и встроенных функций:
ВЫВОД:
Встроенные функции иногда более полезны, чем макросы, поскольку они повышают производительность и безопасны в использовании, а также сокращают накладные расходы на вызов функций. Это просто запрос к компилятору, некоторые функции не будут встроены, например:
что хорошо, потому что это всякий раз, когда компилятор считает, что лучше всего поступить по-другому.
источник
В GCC (насчет других я не уверен) объявление функции встроенной - это просто подсказка для компилятора. В конце концов, компилятор должен решить, будет ли он включать тело функции при каждом ее вызове.
Разница между встроенными функциями и макросами препроцессора относительно велика. Макросы препроцессора - это просто замена текста в конце концов. Вы отказываетесь от многих возможностей компилятора выполнять проверку проверки типов аргументов и возвращаемого типа. Оценка аргументов сильно отличается (если выражения, которые вы передаете в функции, имеют побочные эффекты, вам будет очень интересно провести время за отладкой). Существуют тонкие различия в том, где можно использовать функции и макросы. Например, если бы у меня было:
Где MACRO_FUNC, очевидно, определяет тело функции. Следует проявлять особую осторожность, чтобы она работала правильно во всех случаях, когда функция может быть использована, например, плохо написанный MACRO_FUNC вызовет ошибку в
Там можно было без проблем использовать обычную функцию.
источник
С точки зрения кодирования встроенная функция похожа на функцию. Таким образом, различия между встроенной функцией и макросом такие же, как и различия между функцией и макросом.
С точки зрения компиляции встроенная функция похожа на макрос. Он вводится непосредственно в код, а не вызывается.
В общем, вы должны рассматривать встроенные функции как обычные функции с добавлением некоторой незначительной оптимизации. И, как и большинство оптимизаций, компилятор должен решить, действительно ли он хочет применять его. Часто компилятор с радостью игнорирует любые попытки программиста встроить функцию по разным причинам.
источник
встроенные функции будут вести себя как вызов функции, если в ней существует какой-либо итеративный или рекурсивный оператор, чтобы предотвратить повторное выполнение инструкций. Это очень полезно для экономии общей памяти вашей программы.
источник
Макросы обычно быстрее функций, поскольку они не связаны с фактическими накладными расходами на вызов функций.
Некоторые недостатки макросов: отсутствует проверка типов; трудно отлаживать, поскольку они вызывают простую замену; макрос не имеет пространства имен, поэтому макрос в одном разделе кода может повлиять на другой раздел. Макросы могут вызывать побочные эффекты, как показано в примере CUBE () выше.
Макросы обычно представляют собой один лайнер. Однако они могут состоять более чем из одной строки, в функциях таких ограничений нет.
источник
#define TWO_N(n) 2 << n
этогоcout << CUBE(TWO_N(3 + 1)) << endl;
? (Лучше заканчивать строки вывода,endl
чем начинать их с него.)