Ответы:
Макросы препроцессора - это просто шаблоны подстановки, применяемые к вашему коду. Их можно использовать практически в любом месте вашего кода, потому что они заменяются своими расширениями до начала любой компиляции.
Встроенные функции - это фактические функции, тело которых напрямую вводится в их место вызова. Их можно использовать только там, где уместен вызов функции.
Теперь, что касается использования макросов и встроенных функций в функционально-подобном контексте, имейте в виду, что:
Во-первых, макросы препроцессора просто копируют и вставляют в код перед компиляцией. Таким образом, нет проверки типа , и могут появиться некоторые побочные эффекты
Например, если вы хотите сравнить 2 значения:
#define max(a,b) ((a<b)?b:a)
Побочные эффекты появляются, если вы используете, max(a++,b++)
например ( a
или b
будут увеличиваться вдвое). Вместо этого используйте (например)
inline int max( int a, int b) { return ((a<b)?b:a); }
max(fibonacci(100), factorial(10000))
Встроенная функция расширяется компилятором, а макросы расширяются препроцессором, что является простой текстовой заменой.
При вызове макроса проверка типа не выполняется, в то время как проверка типа выполняется во время вызова функции.
Во время расширения макроса могут возникнуть нежелательные результаты и неэффективность из-за переоценки аргументов и порядка операций. Например
#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*b
int main()
{
// The macro is expended as 2 + 3 * 3 + 5, not as 5*8
printf("%d", MUL(2+3, 3+5));
return 0;
}
// Output: 16`
Ключевое слово return нельзя использовать в макросах для возврата значений, как в случае функций.
Встроенные функции могут быть перегружены
Токены, передаваемые в макросы, могут быть объединены с помощью оператора ##, называемого оператором вставки токена.
Макросы обычно используются для повторного использования кода, а встроенные функции используются для устранения временных затрат (лишнего времени) во время вызова функции (избегая перехода к подпрограмме).
Ключевое отличие - проверка типов. Компилятор проверит, имеет ли то, что вы передаете в качестве входных значений, типы, которые могут быть переданы в функцию. Это не так с макросами препроцессора - они раскрываются перед любой проверкой типов, и это может вызвать серьезные и трудно поддающиеся обнаружению ошибки.
Вот несколько других, менее очевидных моментов.
Чтобы добавить еще одно отличие к уже приведенным: вы не можете пройти через #define
отладчик, но можете пройти через встроенную функцию.
Макросы игнорируют пространства имен. И это делает их злыми.
встроенные функции похожи на макросы (потому что код функции раскрывается в момент вызова во время компиляции), встроенные функции анализируются компилятором, тогда как макросы расширяются препроцессором. В результате есть несколько важных отличий:
В некоторых случаях выражения, переданные в качестве аргументов макросу, можно вычислить более одного раза. http://msdn.microsoft.com/en-us/library/bf6bf4cf.aspx
макросы раскрываются во время предварительной компиляции, вы не можете использовать их для отладки, но вы можете использовать встроенные функции.
- хорошая статья : http://www.codeguru.com/forum/showpost.php?p=1093923&postcount=1
;
Встроенная функция будет поддерживать семантику значения, тогда как макрос препроцессора просто копирует синтаксис. Вы можете получить очень тонкие ошибки с макросом препроцессора, если вы используете аргумент несколько раз - например, если аргумент содержит мутацию типа «i ++», выполнение этого дважды является большим сюрпризом. У встроенной функции этой проблемы не будет.
Встроенная функция синтаксически ведет себя так же, как обычная функция, обеспечивая безопасность типов и область действия для локальных переменных функции и доступ к членам класса, если это метод. Также при вызове встроенных методов вы должны придерживаться частных / защищенных ограничений.
Чтобы понять разницу между макросом и встроенной функцией , во-первых, мы должны знать, что именно они собой представляют и когда мы должны их использовать.
ФУНКЦИИ :
int Square(int x){
return(x*X);
}
int main()
{
int value = 5;
int result = Square(value);
cout << result << endl;
}
Вызовы функций связаны с накладными расходами, поскольку после завершения выполнения функции она должна знать, куда она должна вернуться, а также должна сохранить значение в памяти стека.
Для небольших приложений это не будет проблемой, но давайте возьмем пример финансовых приложений, в которых каждую секунду происходят тысячи транзакций, мы не можем использовать вызовы функций.
MACROS:
# define Square(x) x*x;
int main()
{
int value = 5;
int result = Square(value);
cout << result << endl;
}
int результат = Квадрат (x * x)
Но у макросов есть ошибки, связанные с этим.
#define Square(x) x*x
int main() {
int val = 5;
int result = Square(val + 1);
cout << result << endl;
return 0;
}
Здесь результат 11, а не 36 .
ВСТРОЕННЫЕ ФУНКЦИИ :
inline int Square(int x) {
return x * x;
}
int main() {
using namespace std;
int val = 5;
int result = Square(val + 1);
cout << result << endl;
return 0;
}
Результат 36
Ключевое слово Inline запрашивает компилятор заменить вызов функции телом функции, здесь вывод правильный, потому что сначала вычисляется выражение, а затем передается. Это уменьшает накладные расходы на вызов функции, так как нет необходимости сохранять адрес возврата и стек. Для аргументов функции память не требуется.
Сравнение макросов и встроенных функций:
ВЫВОД:
Встроенные функции иногда более полезны, чем макросы, поскольку они повышают производительность и безопасны в использовании, а также сокращают накладные расходы на вызов функций. Это просто запрос к компилятору, некоторые функции не будут встроены, например:
что хорошо, потому что это всякий раз, когда компилятор считает, что лучше всего поступить по-другому.
В GCC (насчет других я не уверен) объявление функции встроенной - это просто подсказка для компилятора. В конце концов, компилятор должен решить, будет ли он включать тело функции при каждом ее вызове.
Разница между встроенными функциями и макросами препроцессора относительно велика. Макросы препроцессора - это просто замена текста в конце концов. Вы отказываетесь от многих возможностей компилятора выполнять проверку проверки типов аргументов и возвращаемого типа. Оценка аргументов сильно отличается (если выражения, которые вы передаете в функции, имеют побочные эффекты, вам будет очень интересно провести время за отладкой). Существуют тонкие различия в том, где можно использовать функции и макросы. Например, если бы у меня было:
#define MACRO_FUNC(X) ...
Где MACRO_FUNC, очевидно, определяет тело функции. Следует проявлять особую осторожность, чтобы она работала правильно во всех случаях, когда функция может быть использована, например, плохо написанный MACRO_FUNC вызовет ошибку в
if(MACRO_FUNC(y)) {
...body
}
Там можно было без проблем использовать обычную функцию.
С точки зрения кодирования встроенная функция похожа на функцию. Таким образом, различия между встроенной функцией и макросом такие же, как и различия между функцией и макросом.
С точки зрения компиляции встроенная функция похожа на макрос. Он вводится непосредственно в код, а не вызывается.
В общем, вы должны рассматривать встроенные функции как обычные функции с добавлением некоторой незначительной оптимизации. И, как и большинство оптимизаций, компилятор должен решить, действительно ли он хочет применять его. Часто компилятор с радостью игнорирует любые попытки программиста встроить функцию по разным причинам.
встроенные функции будут вести себя как вызов функции, если в ней существует какой-либо итеративный или рекурсивный оператор, чтобы предотвратить повторное выполнение инструкций. Это очень полезно для экономии общей памяти вашей программы.
#include<iostream>
using namespace std;
#define NUMBER 10 //macros are preprocessed while functions are not.
int number()
{
return 10;
}
/*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 64
return 0;
}
Макросы обычно быстрее функций, поскольку они не связаны с фактическими накладными расходами на вызов функций.
Некоторые недостатки макросов: отсутствует проверка типов; трудно отлаживать, поскольку они вызывают простую замену; макрос не имеет пространства имен, поэтому макрос в одном разделе кода может повлиять на другой раздел. Макросы могут вызывать побочные эффекты, как показано в примере CUBE () выше.
Макросы обычно представляют собой один лайнер. Однако они могут состоять более чем из одной строки, в функциях таких ограничений нет.
#define TWO_N(n) 2 << n
этого cout << CUBE(TWO_N(3 + 1)) << endl;
? (Лучше заканчивать строки вывода, endl
чем начинать их с него.)