Відповіді:
Препроцесорні макроси - це лише моделі заміщення, застосовані до вашого коду. Їх можна використовувати майже в будь-якому місці вашого коду, оскільки їх замінюють розширення до початку компіляції.
Вбудовані функції - це фактичні функції, тіло яких безпосередньо вводиться в їх місце виклику. Їх можна використовувати лише там, де виклик функції підходить.
Тепер, що стосується використання макросів та вбудованих функцій у функціональному контексті, слід врахувати, що:
По-перше, макроси препроцесора - це просто "копіювати пасту" в код перед компіляцією. Тому перевірки типу немає , і можуть з’явитися деякі побічні ефекти
Наприклад, якщо ви хочете порівняти 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))
що більший обчислюється вдвічі :(
Функція Inline розширюється компілятором, де як макроси розширюються за допомогою препроцесора, що є просто текстовою підстановкою.
Під час виклику макросу під час виклику функції не відбувається перевірка типу
Небажані результати та неефективність можуть виникати під час розширення макросу через переоцінку аргументів та порядку операцій. Наприклад
#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 не може використовуватися в макросах для повернення значень, як у випадку функцій.
Вбудовані функції можуть бути перевантажені
Маркери, передані макросам, можна об'єднати за допомогою оператора ##, який називається оператором Token-Pasting.
Макроси, як правило, використовуються для повторного використання коду, коли вбудовані функції використовуються для усунення накладних витрат часу (перевищення часу) під час виклику функції (уникаючи переходу на підпрограму).
Ключова різниця - перевірка типу. Компілятор перевірить, чи є те, що ви передаєте як вхідні значення, типів, які можна передавати у функцію. Це не вірно з макросами препроцесора - вони розширюються до будь-якої перевірки типу, і це може спричинити серйозні та важкі виявлення помилок.
Ось окреслено кілька інших менш очевидних моментів.
Щоб додати ще одну відмінність до тих, що вже були задані: ви не можете перейти через #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;
}
З ним пов'язані накладні виклики функцій, оскільки після завершення виконання функції він повинен знати, куди він повинен повернутися, а також потрібно зберігати значення в пам'яті стека.
Для невеликих додатків це не буде проблемою, але давайте приклад фінансових програм, де тисячі транзакцій відбуваються щосекунди, ми не можемо переходити з функціональними дзвінками.
Макроси:
# 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
Вбудоване ключове слово запитує компілятор замінити виклик функції тілом функції, тут вихід правильний, оскільки він спочатку оцінює вираз, а потім передає. Це зменшує накладні виклики функції, оскільки немає необхідності зберігати зворотну адресу та стек пам'ять не потрібна для аргументів функції.
Порівняння між макросами та вбудованими функціями:
ВИСНОВОК:
Вбудовані функції іноді корисніші, ніж макроси, оскільки вони покращують продуктивність, безпечні у використанні та зменшують накладні виклики функцій. Це просто запит до компілятора, певні функції не будуть покладені на зразок:
що добре, тому що коли-небудь компілятор вважає, що краще робити речі іншим способом.
У 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
ніж починати їх з нього.)