Я не розумію добре поняття макросу. Що таке макрос? Я не розумію, чим він відрізняється від функції? І функція, і макрос містять блок коду. Тож як відрізняються макро та функції?
Я не розумію добре поняття макросу. Що таке макрос? Я не розумію, чим він відрізняється від функції? І функція, і макрос містять блок коду. Тож як відрізняються макро та функції?
Відповіді:
Я хотів би додати наступне уточнення після дотримання поляризаційної схеми голосування у цій відповіді.
Відповідь не була написана з урахуванням технічної точності та широкого узагальнення. Це була скромна спроба пояснити простою мовою різницю між макросами та функціями для новачка, який програмує, не намагаючись бути повним чи точним (адже це далеко не точне). Мова програмування на C мав на увазі, коли я складав відповідь, але вибачте за те, що я не згадав її чітко і викликав будь-яку (потенційну) плутанину.
Я дуже розглядаю відповідь, яку поділяє Йорг W Міттаг . Це було уважно, читаючи його (наскільки менше я знаю), і я виступив проти цього незабаром після того, як він був розміщений. Я щойно розпочав роботу з обміну стеками програмного забезпечення, і досвід та обговорення поки що були дуже проникливими.
Я залишу цю відповідь тут, оскільки це може бути корисним для інших новачків із розробки програмного забезпечення, намагаючись зрозуміти концепцію, не занурюючись в технічну точність.
І макро, і функція являють собою самостійну одиницю коду. Вони обидва - це інструмент, який допомагає в модульному дизайні програми. З точки зору програміста, який пише вихідний код, вони виглядають досить схожими. Однак вони відрізняються тим, як поводяться з ними під час життєвого циклу виконання програми.
Макрос визначається один раз і використовується у багатьох місцях програми. Макрос стає розширеним вбудованим на етапі попередньої обробки. Таким чином, технічно він не залишається окремим об'єктом після складання вихідного коду. Висловлювання в макроозначенні стають частиною інструкцій програми, як і інші твердження.
Мотив написання макросу - спрощення написання та керування вихідним кодом для програміста. Макроси, як правило, бажані для більш простих завдань, коли написання повноцінної функції буде покаранням за накладні витрати / виконання. Прикладами ситуацій, коли макрос є кращим над функцією, є:
Використання постійних значень (наприклад, математичних чи наукових значень) або певного програмного параметра.
Друк журналів або обробка тверджень.
Проведення простих розрахунків або перевірок стану.
Під час використання макросу легко вносити зміни / виправлення в одне місце, які миттєво доступні скрізь, де макрос використовується в програмі. Для того, щоб зміни набрали чинності, потрібна проста перекомпіляція програми.
З іншого боку, функціональний код складається як окремий блок в межах програми і завантажується в пам'ять під час виконання програми лише за необхідності. Код функції зберігає свою незалежну ідентичність від решти програми. Завантажений код повторно використовується, якщо функція викликається більше одного разу. Коли в запущеній програмі виникає виклик функції, управління передається їй підсистемою виконання, і контекст запущеної програми (повернення адреси інструкції) зберігається.
Однак, існує невеликий штраф за продуктивність, з яким потрібно зіткнутися під час виклику функції (переключення контексту, збереження зворотної адреси основних інструкцій програми, проходження параметрів та обробка значень повернення тощо). Отже, використання функції бажано лише для складних блоків коду (проти макросів, які обробляють більш прості випадки).
Маючи досвід, програміст приймає розумне рішення щодо того, чи буде фрагмент коду добре підходити як макрос або функція в загальній архітектурі програми.
На жаль, в програмуванні існує багато різних застосувань терміна "макро".
У сімействі мов Lisp та мовах, натхнених ними, а також багатьох сучасних функціональних або функціональних мовах, таких як Scala та Haskell, а також деяких імперативних мов, таких як Boo, макрос - це фрагмент коду, який працює під час компіляції. (або принаймні до часу виконання для реалізацій без компілятора) і може перетворити абстрактне дерево синтаксису (або будь-який еквівалент певної мови, наприклад, у Lisp, це були б S-вирази) під час компіляції. Наприклад, у багатьох реалізаціях схеми,for - це макрос, який розширюється на кілька викликів до тіла. У мовах, що мають статичний тип, макроси часто захищені від типу, тобто вони не можуть створювати код, який недостатньо набраний.
У сімействі мов C макроси більше схожі на підстановку тексту. Що також означає, що вони можуть створювати код, який не є набраним або навіть не синтаксично законним.
У макроскладальниках "макроси" посилаються на "віртуальні вказівки", тобто інструкції, які ЦП не підтримує, але вони є корисними, і тому асемблер дозволяє використовувати ці вказівки і розширює їх на кілька інструкцій, які розуміє ЦП. .
У сценарії програми «макрос» позначає низку дій, які користувач може «записати» та «відтворити».
Усі вони є певним чином виконуваним кодом, а це означає, що їх можна розглядати як функції. Однак у випадку макросів Lisp, наприклад, їх введення та вихід є фрагментами програми. У випадку з C їх введення та вихід є лексемами. Перші три також мають дуже важливу відмінність, яку вони виконують під час компіляції . Насправді макроси C-препроцесора, як випливає з назви, фактично виконуються ще до того, як код навіть дійде до компілятора .
У мові C сім'ї Макроозначення , команда препроцесора, вказує параметризованим шаблон коду , який заміщається на макровизове без скомпільовано у визначенні. Це означає, що всі вільні змінні повинні бути пов'язані в контексті виклику макросів. Аргументи параметрів із таким побічним ефектом, як, наприклад, i++можна повторити, використовуючи параметр у множині. Текстова підміна аргументу 1 + 2деякого параметра xвідбувається перед компіляцією і може спричинити несподівану поведінку x * 3( 7io 9). Помилка в тілі макроса визначення макроса з’явиться лише після компіляції при виклику макросу.
В визначення функції вказує код з вільними змінними , пов'язаними з контекстом функції організму; не виклик функції .
Однак макрос, мабуть, негативний, забезпечує доступ до виклику, номера лінії та вихідного файлу, аргументу у вигляді рядка .
У трохи більш абстрактних термінах макрос - це синтаксис, а функція - це дані. Функція (у рефераті) інкапсулює деяку трансформацію даних. Він приймає свої аргументи як оцінені дані, виконує деякі операції над ними та повертає результат, який також є просто даними.
Макрос на противагу має деякий неоцінений синтаксис і діє на цьому. Для мов, подібних С, синтаксис надходить на рівні лексеми. Для мов з LISP-подібними макросами вони отримують синтаксис, представлений як AST. Макрос повинен повернути новий фрагмент синтаксису.
Макрос зазвичай відноситься до чого - то , що розгорнутому на місці , замінивши макрос «виклик» під час компіляції або попередньої обробки з окремими інструкціями в цільовому мовою. Під час виконання, як правило, не буде вказано, де макрос починається і закінчується.
Це відрізняється від підпрограми , яка є частиною коду для багаторазового використання, яка розташована окремо в пам'яті, якій управління передається під час виконання . "Функції", "процедури" та "методи" в більшості мов програмування підпадають під цю категорію.
Як йдеться у відповіді Йорга У Міттага , точні деталі різняться між мовами: у деяких, таких як C, макрос виконує заміну тексту у вихідному коді; у деяких, таких як Лісп, він виконує маніпуляції з посередницькою формою, як Абстрактне синтаксичне дерево. Також є кілька сірих областей: деякі мови мають позначення "вбудованих функцій", які визначені як функція, але розширені на складену програму як макрос.
Більшість мов спонукають програмістів до роздумів про кожну підпрограму поодиноко, визначаючи типи контрактів на входи та виходи та приховуючи іншу інформацію про код виклику. Часто виникає вплив на ефективність виклику підпрограми, макроси якої не виникають, і макроси можуть мати можливість маніпулювати програмою так, як підпрограма не може. Тому макроси можна вважати "нижчим рівнем", ніж підпрограми, оскільки вони працюють на менш абстрактній основі.
Макрос виконується під час компіляції, а функція виконується під час виконання.
Приклад:
#include <stdio.h>
#define macro_sum(x,y) (x+y)
int func_sum(x,y) {
return x+y;
}
int main(void) {
printf("%d\n", macro_sum(2,3));
printf("%d\n", func_sum(2,3));
return 0;
}
Тому під час компіляції код фактично змінюється на:
#include <stdio.h>
int func_sum(x,y) {
return x+y;
}
int main(void) {
printf("%d\n", (2+3));
printf("%d\n", func_sum(2,3));
return 0;
}