Вбудовані функції проти макросів препроцесора


Відповіді:


127

Препроцесорні макроси - це лише моделі заміщення, застосовані до вашого коду. Їх можна використовувати майже в будь-якому місці вашого коду, оскільки їх замінюють розширення до початку компіляції.

Вбудовані функції - це фактичні функції, тіло яких безпосередньо вводиться в їх місце виклику. Їх можна використовувати лише там, де виклик функції підходить.

Тепер, що стосується використання макросів та вбудованих функцій у функціональному контексті, слід врахувати, що:

  • Макроси не є безпечними для типу, і їх можна розширити незалежно від того, чи вони синтаксично правильні - фаза компіляції повідомить про помилки, що виникають внаслідок проблем із розширенням макросу.
  • Макроси можна використовувати в контексті, де ви не очікуєте, що спричинить проблеми
  • Макроси є більш гнучкими, оскільки вони можуть розширювати інші макроси, тоді як вбудовані функції не обов'язково роблять це.
  • Макроси можуть спричинити побічні ефекти через їх розширення, оскільки вхідні вирази копіюються скрізь, де вони з’являються в шаблоні.
  • Не завжди гарантується, що вбудована функція вбудована - деякі компілятори роблять це лише у складах випусків або коли вони спеціально налаштовані для цього. Крім того, в деяких випадках вкладення може бути неможливим.
  • Вбудовані функції можуть надавати область для змінних (особливо статичних), препроцесорні макроси можуть робити це лише в кодових блоках {...}, а статичні змінні не будуть вести себе точно так само.

39
Вбудована функція не завжди гарантується, що вона буде вбудована: Тому що компілятор не буде вбудованим, якщо це буде створювати повільніший код і т. Д. Компілятор робить багато аналізу, який Інженер не може і робить правильно.
Мартін Йорк

14
Я вважаю, що рекурсивні функції - це ще один приклад, коли більшість компіляторів ігнорує вбудовування.
Л.Бушкін

Чи є в цьому випадку важливі відмінності C порівняно з C ++?
rzetterberg

7
Один момент, який не згадується, - це те, що на вкладку можуть впливати компіляційні прапори. Наприклад, коли ви будуєте для максимальної швидкості (наприклад, GCC -O2 / -O3), компілятор вирішить вбудувати багато функцій, але коли ви будуєте для мінімального розміру (-Os), як правило, вбудовані функції, що називаються лише один раз (або дуже маленькі функції) ). З макросами такого вибору немає.
dbrank0

Макроси не можуть охоплюватися специфікатором доступу (наприклад, приватним або захищеним), хоча можливі вбудовані функції.
Хіт

78

По-перше, макроси препроцесора - це просто "копіювати пасту" в код перед компіляцією. Тому перевірки типу немає , і можуть з’явитися деякі побічні ефекти

Наприклад, якщо ви хочете порівняти 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); }

3
Просто хочу додати до свого прикладу, що крім побічного ефекту макро також може вводити додаткове робоче навантаження, врахуйте, max(fibonacci(100), factorial(10000))що більший обчислюється вдвічі :(
watashiSHUN

Усі говорять про перевірку типу, але тільки ви подали приклад у реальному світі, тому я підтримую цю відповідь.
Іванзіньо

16

Функція 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.

  • Макроси, як правило, використовуються для повторного використання коду, коли вбудовані функції використовуються для усунення накладних витрат часу (перевищення часу) під час виклику функції (уникаючи переходу на підпрограму).


13

Ключова різниця - перевірка типу. Компілятор перевірить, чи є те, що ви передаєте як вхідні значення, типів, які можна передавати у функцію. Це не вірно з макросами препроцесора - вони розширюються до будь-якої перевірки типу, і це може спричинити серйозні та важкі виявлення помилок.

Ось окреслено кілька інших менш очевидних моментів.


11

Щоб додати ще одну відмінність до тих, що вже були задані: ви не можете перейти через #defineналагоджувач, але ви можете перейти через вбудовану функцію.



3

вбудовані функції схожі на макроси (оскільки код функції розгорнуто в точці виклику під час компіляції), вбудовані функції аналізуються компілятором, тоді як макроси розширюються препроцесором. Як результат, є кілька важливих відмінностей:

  • Вбудовані функції дотримуються всіх протоколів безпеки типу, що застосовуються до звичайних функцій.
  • Вбудовані функції задаються за допомогою того ж синтаксису, що і будь-яка інша функція, за винятком того, що вони включають ключове слово вбудований в декларацію функції.
  • Вирази, передані як аргументи для вбудованих функцій, оцінюються один раз.
  • У деяких випадках вирази, передані як аргументи макросам, можна оцінювати не один раз. http://msdn.microsoft.com/en-us/library/bf6bf4cf.aspx

  • макроси розгортаються на час перед компіляцією, ви не можете використовувати їх для налагодження, але ви можете використовувати вбудовані функції.

- хороша стаття : http://www.codeguru.com/forum/showpost.php?p=1093923&postcount=1

;


2

Вбудована функція підтримуватиме значення семантики значень, тоді як макрос препроцесора просто копіює синтаксис. Ви можете отримати дуже тонкі помилки за допомогою макроса препроцесора, якщо аргумент використовувати кілька разів - наприклад, якщо аргумент містить мутації, як "i ++", який має виконати двічі, - це дуже несподівано. Вбудована функція не матиме цієї проблеми.


1

Вбудована функція поводиться синтаксично так само, як і звичайна функція, забезпечуючи безпеку типу та область для функціонування локальних змінних та доступ до членів класу, якщо це метод. Також при виклику вбудованих методів необхідно дотримуватися приватних / захищених обмежень.


1

Щоб знати різницю між макро- та вбудованою функцією , по-перше, ми повинні знати, що саме вони є, і коли ми повинні їх використовувати.

ФУНКЦІЇ :

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

Вбудоване ключове слово запитує компілятор замінити виклик функції тілом функції, тут вихід правильний, оскільки він спочатку оцінює вираз, а потім передає. Це зменшує накладні виклики функції, оскільки немає необхідності зберігати зворотну адресу та стек пам'ять не потрібна для аргументів функції.

Порівняння між макросами та вбудованими функціями:

  1. Макроси працюють за допомогою підстановки, тоді як у вбудованих функціях виклик функції замінюється тілом.
  2. Макроси схильні до помилок через заміну, тоді як вбудовані функції безпечні для використання.
  3. Макроси не мають адреси, тоді як вбудовані функції мають адресу.
  4. Макроси важко використовувати з кількома рядками коду, тоді як вбудовані функції - ні.
  5. У C ++ макроси не можна використовувати з членами, тоді як вбудована функція може бути.

ВИСНОВОК:

Вбудовані функції іноді корисніші, ніж макроси, оскільки вони покращують продуктивність, безпечні у використанні та зменшують накладні виклики функцій. Це просто запит до компілятора, певні функції не будуть покладені на зразок:

  • великі функції
  • функції, що мають занадто багато умовних аргументів
  • рекурсивний код і код з петлями і т.д.

що добре, тому що коли-небудь компілятор вважає, що краще робити речі іншим способом.


Тільки як зауваження: макрос можна виправити, щоб оцінити на одне число з дужками. Однак це все ще схильне до помилок, оскільки потрібно думати про абсолютну тупу підстановку та всі випадки під час впровадження.
Майк

0

У GCC (я не впевнений у інших), декларування функції вбудованою - лише натяк на компілятор. В кінці дня все ще залишається за рішенням компілятора вирішити, чи включає він тіло функції, коли він викликається.

Різниця між вбудованими функціями та макросами препроцесора порівняно велика. Препроцесорні макроси - це лише заміна тексту в кінці дня. Ви даєте багато можливостей компілятору виконувати перевірку перевірки типу аргументів та типу повернення. Оцінка аргументів сильно відрізняється (якщо вирази, які ви передаєте у функції, мають побічні ефекти, вам буде дуже цікаво відладжувати час). Існують тонкі відмінності щодо того, де можна використовувати функції та макроси. Наприклад, якщо б у мене було:

#define MACRO_FUNC(X) ...

Де MACRO_FUNC очевидно визначає тіло функції. Необхідно бути особливо обережним, щоб він працював правильно у всіх випадках, якщо функція може бути використана, наприклад, неправильно написаний MACRO_FUNC призведе до помилки в

if(MACRO_FUNC(y)) {
 ...body
}

Там нормальна функція може бути використана без проблем.


0

З точки зору кодування, вбудована функція схожа на функцію. Таким чином, відмінності між вбудованою функцією та макросом такі ж, як і відмінності між функцією та макросом.

З точки зору компіляції, вбудована функція схожа на макрос. Він вводиться безпосередньо в код, не викликається.

Загалом, ви повинні розглянути вбудовані функції як звичайні функції з деякими незначними оптимізаціями, змішаними. І як і більшість оптимізацій, компілятор повинен вирішити, чи дійсно це стосується його застосування. Часто компілятор із різних причин з радістю ігнорує будь-які спроби програміста вбудувати функцію.


0

вбудовані функції будуть вести себе як виклик функції, якщо в ньому є якийсь ітеративний або рекурсивний вираз, щоб запобігти повторному виконанню інструкцій. Це досить корисно для збереження загальної пам'яті вашої програми.


-1
#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ніж починати їх з нього.)
Джонатан Леффлер,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.