Я не очень хорошо понимаю концепцию макроса. Что такое макрос? Я не понимаю, как это отличается от функции? И функция, и макрос содержат блок кода. Так чем же отличаются макрос и функция?
programming-practices
Эдоардо Соргентоне
источник
источник
Ответы:
Заметка
Я хотел бы добавить следующее разъяснение после наблюдения поляризационной схемы голосования по этому ответу.
Ответ не был написан с учетом технической точности и широкого обобщения. Это была скромная попытка объяснить простым языком разницу между макросами и функциями новичку в программировании, не пытаясь быть полным или точным (на самом деле это далеко не так). Язык программирования C был в моей голове при составлении ответа, но я извиняюсь за то, что не упомянул его четко и не вызвал (потенциальную) путаницу.
Я действительно расцениваю ответ, которым поделился Йорг Миттаг . Это было проницательное чтение (насколько я знаю), и я проголосовал за него вскоре после того, как он был опубликован. Я только начал изучать стек разработки программного обеспечения, и опыт и обсуждения до сих пор были действительно проницательными.
Я оставлю этот ответ здесь, так как он может быть полезен для других новичков в разработке программного обеспечения, пытающихся понять концепцию, не увязая в технической точности.
И макрос, и функция представляют собой автономную единицу кода. Они оба являются инструментом, который помогает в модульном дизайне программы. С точки зрения программиста, пишущего исходный код, они выглядят довольно похоже. Однако они отличаются тем, как они обрабатываются в течение жизненного цикла выполнения программы.
Макрос определяется один раз и используется во многих местах программы. Макрос становится расширенным на этапе предварительной обработки. Таким образом, технически он не остается отдельной сущностью после компиляции исходного кода. Операторы в макроопределении становятся частью инструкций программы, как и другие операторы.
Мотивом написания макроса является упрощение написания и управления исходным кодом для программиста. Макросы, как правило, желательны для более простых задач, где написание полноценной функции может привести к снижению производительности / времени выполнения. Примеры ситуаций, когда макрос предпочтительнее функции:
Использование постоянных значений (таких как математические или научные значения) или какого-либо специального программного параметра.
Печать сообщений журнала или обработка утверждений.
Выполнение простых расчетов или проверок условий.
При использовании макроса легко вносить изменения / исправления в одном месте, которые мгновенно доступны везде, где макрос используется в программе. Чтобы изменения вступили в силу, необходима простая перекомпиляция программы.
Код функции, с другой стороны, компилируется как отдельный модуль в программе и загружается в память во время выполнения программы, только если это необходимо. Код функции сохраняет свою независимость от остальной части программы. Загруженный код повторно используется, если функция вызывается более одного раза. Когда вызов функции встречается в работающей программе, управление передается ей подсистемой времени выполнения, и контекст работающей программы (адрес инструкции возврата) сохраняется.
Однако при вызове функции необходимо немного снизить производительность (переключение контекста, сохранение адреса возврата основных команд программы, передача параметров и обработка возвращаемых значений и т. Д.). Следовательно, использование функции желательно только для сложных блоков кода (против макросов, которые обрабатывают более простые случаи).
Имея опыт, программист принимает взвешенное решение о том, подойдет ли фрагмент кода в качестве макроса или функции в общей архитектуре программы.
источник
К сожалению, в программировании существует множество различных вариантов использования термина «макрос».
В семействе языков Lisp и воодушевленных ими языках, а также во многих современных функциональных или функционально-вдохновленных языках, таких как Scala и Haskell, а также в некоторых императивных языках, таких как Boo, макрос - это фрагмент кода, который выполняется во время компиляции. (или, по крайней мере, до времени выполнения для реализаций без компилятора) и может преобразовать Абстрактное Синтаксическое Дерево (или какой-либо эквивалент в конкретном языке, например, в Лиспе, это будут S-выражения) во что-то другое во время компиляции. Например, во многих реализациях Scheme
for
это макрос, который расширяется в несколько обращений к телу. В статически типизированных языках макросы часто являются типобезопасными, то есть не могут генерировать код, который не является типизированным.В семействе языков C макросы больше похожи на текстовую замену. Это также означает, что они могут создавать код, который не является типизированным или даже синтаксически недопустимым.
В макросе-ассемблере «макросы» означают «виртуальные инструкции», то есть инструкции, которые ЦПУ не поддерживает изначально, но которые полезны, и поэтому ассемблер позволяет использовать эти инструкции и расширит их до нескольких инструкций, которые понимает ЦП. ,
В сценариях приложения «макрос» относится к серии действий, которые пользователь может «записать» и «воспроизвести».
Все это в некотором смысле виды исполняемого кода, что означает, что они могут в некотором смысле рассматриваться как функции. Однако в случае макросов Lisp, например, их ввод и вывод являются фрагментами программы. В случае C их вход и выход являются токенами. Первые три также имеют очень важное различие в том, что они выполняются во время компиляции . Фактически, макросы препроцессора C, как следует из названия, фактически выполняются еще до того, как код достигнет компилятора .
источник
В семействе языков C определение макроса , команда препроцессора, задает параметризованный шаблон кода, который подставляется при вызове макроса без компиляции в определении. Это означает, что все свободные переменные должны быть связаны в контексте вызова макроса. Аргументы параметра с побочным эффектом
i++
могут повторяться путем многократного использования параметра. Текстовая подстановка аргумента1 + 2
некоторого параметраx
происходит перед компиляцией и может вызвать непредвиденное поведениеx * 3
(7
io9
). Ошибка в теле макроса определения макроса будет отображаться только при компиляции при вызове макроса.Определение функции определяет код со свободными переменными, привязанными к контексту тела функции; не вызов функции .
Макрос, однако, на первый взгляд отрицательный, обеспечивает доступ к вызову, номеру строки и исходному файлу, аргументу в виде строки .
источник
В несколько более абстрактных терминах макрос - это синтаксис как функция для данных. Функция (абстрактно) инкапсулирует некоторые преобразования в данных. Он принимает свои аргументы в качестве оцененных данных, выполняет с ними некоторые операции и возвращает результат, который также является просто данными.
Макрос, напротив, принимает некоторый неоцененный синтаксис и оперирует этим. Для языков, подобных C, синтаксис вводится на уровне токенов. Для языков с LISP-подобными макросами они получают синтаксис, представленный в виде AST. Макрос должен возвращать новый фрагмент синтаксиса.
источник
Макрос обычно относится к чему - то , что развернутом на месте , заменив макрос «вызов» во время компиляции или предварительной обработки с отдельными инструкциями в целевом языке. Во время выполнения, как правило, не будет указаний того, где макрос начинается и заканчивается.
Это отличается от подпрограммы , представляющей собой фрагмент кода многократного использования, который расположен отдельно в памяти, которому управление передается во время выполнения . «Функции», «процедуры» и «методы» в большинстве языков программирования попадают в эту категорию.
Как говорится в ответе Йорга В. Миттага , точные детали различаются в зависимости от языка: в некоторых, таких как C, макрос выполняет подстановку текста в исходном коде; в некоторых, таких как Lisp, он выполняет манипуляции с промежуточной формой, такой как абстрактное синтаксическое дерево. Есть также некоторые серые области: некоторые языки имеют обозначения для «встроенных функций», которые определены как функция, но расширены в скомпилированную программу как макрос.
Большинство языков побуждают программистов обдумывать каждую подпрограмму изолированно, определяя контракты типов для входов и выходов и скрывая другую информацию о вызывающем коде. Часто вызывается подпрограмма, вызывающая подпрограмму, которую не несут макросы, и макросы могут манипулировать программой так, как не может подпрограмма. Поэтому макросы можно считать «более низким уровнем», чем подпрограммы, поскольку они работают на менее абстрактной основе.
источник
Макрос выполняется во время компиляции, а функция выполняется во время выполнения.
Пример:
Таким образом, во время компиляции код фактически изменяется на:
источник