c макросы с параметрами

Макросы

Определение макросов

П еред тем как программа будет скомпилирована (или не будет, если найдены ошибки), текст программы обрабатывается препроцессором. Препроцессор позволяет изменять текст программы, используя специальные директивы.
Директива #define определяет новый макрос. Макрос, или макроподстановка, будет заменена в коде программы своим телом. Например, мы часто пользовались макросом

и после этого использовали SIZE вместо размера массива.
Макрос может иметь любое допустимое имя и обычно его пишут прописными буквами для того, чтобы отличать от переменных и констант.

Макрос подставляется непосредственно в текст вашей программы. То есть, если у вас был код

то он будет заменён на код

Макросы могут иметь аргументы.

Несмотря на то, что этот код работает, в нём есть ошибки. Макроподстановка – это именно подстановка:

С одной стороны, этот макрос должен делать программу быстрее, если заменить им вызов функции. Но на деле работать он будет медленнее. Макрос развернётся в следующий код
4,18879020 * (halfA + halfB) * (halfA + halfB) * (halfA + halfB)
итого, три раза будет вызвано сложение. Вот ещё пример ошибки

В данном случае будет выведено 19 вместо 45, так как макрос будет раскрыт в выражение
2 + 3 * 4 + 5 == 2 + 12 + 5 == 19
Решением будет следующий макрос:

И ещё одна ошибка, которая также встречается очень часто. Давайте напишем макрос, который будет выводить на печать массив. Мы воспользуемся им в сортировке пузырьком, чтобы видеть, как изменяется массив во время сортировки.

Этот пример работать не будет. Дело в том, что он использует переменную i, которая уже занята. Для корректной работы необходимо локализовать переменную. Для этого тело макроса нужно обернуть фигурными скобками и внутри задать переменную

Важно отметить ещё одну особенность языка. Строки в си ограничены двойными кавычками, но их можно конкатенировать просто написав рядом, например

Таким образом, можно объявлять макросы и использовать их следующим образом

Условные конструкции

Такая конструкция будет выполнять первую ветвь, если определён макрос с заданным именем. Например, таким образом можно создавать макрос, который будет выводить отладочную информацию.

Кроме директивы #ifdef используется директива #ifndef (if not defined), он работает также, но первая ветвь работает только в случае, если макрос не определён. Также, как и с условными конструкциями, макрос может и не содержать ветви else.

Предопределённые макросы.

Использование препроцессора для инициализации объектов

В си директива include вставляет кусок кода в то место, где она указана. Это значит, что можно использовать директиву для начальной инициализации объектов, если, например, они слишком большие. Представленный ниже код валиден.

array.txt в той же директории

1, 2, 3, 4, 5, 6, 7, 8, 9, 10

Макросы с переменным числом параметров

В С11 определён новый тип макросов – макросы с переменным числом параметров. Определяется он похоже на функции с переменным числом параметров. Обращение к параметрам происходит через макрос __VA_ARGS__. __VA_ARGS__ заменяется на переданные аргументы. Пример: имеется функция, собирающая односвязный список из массива.

Необходимо написать макрос, который бы собрал список, при этом количество параметров можно было изменять.

Функция fromArray получает три аргумента – указатель на узел, массив и его размер. Мы хотим избавиться от размера и массива. Тем не менее, всё равно придётся передавать тип массива, чтобы автоматически можно было изменять его размер.

Макрос принимает два обязательных параметра – имя узла и название типа. Оставшихся параметров будет произвольное число, они перечисляются через запятую.

внутри блока (области, ограниченной фигурными скобками) создаём массив и инициализируем его. При этом длина массива определяется автоматически.

Вызываем функцию, передавая ей в качестве аргументов указатель на узел, массив, который мы только что создали и размер. Так как тип массива известен и массив статический, то количество элементов находится элементарно.

таким образом, будет трансформирован в

Стрингизация и конкатенация макросов

Е сли во время создания макроса появилась необходимость в том, чтобы сделать из макроса строку или соединять макросы в один, то можно воспользоваться операторами # и ##. Оператор # превращает переданное значение в строку. Оператор ## соединяет элементы макроса. Например, пусть у нас имеется структура, которая используется для парсинга комманд. Она состоит из строки (имени команды) и самой команды. При этом мы решили, что имя функции должно состоять из имени комманды плюс _command :

Для сокращения кода можно объявить такой макрос

Здесь #NAME превращает переданный параметр в строку, а NAME ## _command конкатенирует параметр с _command. Весь код:

Этот макрос выведет на печать __somedata

Источник

Макросы в С и С++

Что есть макросы?

В языках С и С++ есть такой механизм, как препроцессор. Он обрабатывает исходный код программы ДО того, как она будет скомпилированна. У перпроцессора есть свои директивы, такие как #include, #pragma, #if и тд. Но нам интересна только директива #define.

В языке Си довольно распространенной практикой является объявление глобальных констант с помощью директивы #define:

А потом, на этапе препроцессинга все использования PI будут заменены указанным значением:

После препроцессинга, который по сути является банальной подстановкой, это выражение превратится в:

О макросах важно понимать, что область видимости у них такая же, как у нестатических функций в языке Си, то есть они видны везде, куда их «заинклюдили». Однако в отличии от функций, объявление макроса можно отменить:

После этой строчки обращаться к PI будет уже нельзя.

Макросы с параметрами

Макрос может состоять не только из одного выражения. Например макрос, который меняет значения двух переменных:

Поскольку мы первым параметром передаем тип, данный макрос будет работать с переменными любого типа:

Макросы так же можно записывать в несколько строк, но тогда каждая строка, кроме последней, должна заканчиваться символом ‘\’:

Параметр макроса можно превратить в строку, добавив перед ним знак ‘#’:

А еще параметр можно приклеить к чему-то еще, чтобы получился новый идентификатор. Для этого между параметром и тем, с чем пы его склеиваем, нужно поставить ‘##’:

Техника безопасности при работе с макросами

Есть несколько основных правил, которые нужно соблюдать при работе с макросами.

1. Параметрами макросов не должны быть выражения и вызовы функций.

Ранее я уже объявлял макрос MAX. Но что получится, если попытаться вызвать его вот так:

Со стороны все выглядит нормально, но вот что получится в результате макроподстановки:

В итоге переменная max будет равна не 4, как мы ожидали, а 3. Потом можно уйму времени потратить, отлавливая эту ошибку. Так что в качестве аргумента макроса нужно всегда передавать уже конечное значение, а не какое-то выражение или вызов функции. Иначе выражение или функция будут вычислены столько раз, сколько используется этот параметр в теле макроса.

2. Все аргументы макроса и сам макрос должны быть заключены в скобки.

Это правило я уже нарушил при написании макроса MAX. Что получится, если мы захотим использовать этот макрос в составе какого-то математического выражения?

Читайте также:  на нержавейке появились черные пятна что делать

По логике, переменная result должна будет иметь значение 9, однако вот что мы получаем в результате макроподстановки:

И переменная result внезапно примет значение 1. Чтобы такого не происходило, макрос MAX должен быть объявлен следующим образом:

В таком случае все действия произойдут в нужном порядке.

3. Многострочные макросы должны иметь свою область видимости.

Например у нас есть макрос, который вызывает две функции:

А теперь попробуем использовать этот макрос в таком контексте:

После макроподстановки мы увидим вот такую картину:

Поскольку в условии цикла стоит ноль, он отработает ровно один раз. Это делается, во первых, для того, чтобы у тела макроса появилась своя область видимости, ограниченная телом цикла, а во вторых, чтобы сделать вызов макроса более привычным, потому что теперь после MACRO() нужно будет ставить точку с запятой. Если бы мы просто ограничили тело макроса фигурными скобками, точку с запятой после его вызова поставить бы не получилось.

Еще немного примеров

Теперь чтобы нагенерировать таких функций для нужных нам типов, нужно просто использовать пару раз этот макрос в глобальной зоне видимости:

Таким образом у нас получился аналог шаблонов из С++. Но стоит сразу обратить внимание, что данный способ не подойдет для типов, название которых состоит более чем из одного слова, например long long или unsigned short, потому что не получится нормально склеить название функции (sum_##type). Для этого сперва придется объявить для них новый тип, состоящий из одного слова.

Но все же, если есть возможность обходится без макросов, лучше обходится без макросов. Даже если ты прекрасно усвоил, как они работают, и понимаешь их силу, это никак не страхует тебя от того, что ты допустишь какую-то глупую ошибку при объявлении макроса, а потом 20 минут потратишь на ее поиски. Так что не стоит оборачивать в макросы вообще все, что только можно.

Источник

Макросы в С и С++

Что есть макросы?

В языках С и С++ есть такой механизм, как препроцессор. Он обрабатывает исходный код программы ДО того, как она будет скомпилирована. У перпроцессора есть свои директивы, такие как #include, #pragma, #if и тд. Но нам интересна только директива #define.

В языке Си довольно распространенной практикой является объявление глобальных констант с помощью директивы #define:

А потом, на этапе препроцессинга, все использования PI будут заменены указанным значением:

После препроцессинга, который по сути является банальной подстановкой, это выражение превратится в:

О макросах важно понимать, что область видимости у них такая же, как у нестатических функций в языке Си, то есть они видны везде, куда их «заинклюдили». Однако в отличии от функций, объявление макроса можно отменить:

После этой строчки обращаться к PI будет уже нельзя.

Макросы с параметрами

Макрос может состоять не только из одного выражения. Например макрос, который меняет значения двух переменных:

Поскольку мы первым параметром передаем тип, данный макрос будет работать с переменными любого типа:

В подобных макросах, вместо передачи типа аргументов первым параметром, полезно использовать оператор typeof в языке C или decltype в C++. С их помощью можно удобно объявлять переменную tmp того же типа, что и переданные аргументы:

Макросы также можно записывать в несколько строк, но тогда каждая строка, кроме последней, должна заканчиваться символом ‘\’:

Параметр макроса можно превратить в строку, добавив перед ним знак ‘#’:

А еще параметр можно приклеить к чему-то еще, чтобы получился новый идентификатор. Для этого между параметром и тем, с чем мы его склеиваем, нужно поставить ‘##’:

Техника безопасности при работе с макросами

Есть несколько основных правил, которые нужно соблюдать при работе с макросами.

1. Параметрами макросов не должны быть выражения и вызовы функций.

Ранее я уже объявлял макрос MAX. Но что получится, если попытаться вызвать его вот так:

Со стороны все выглядит нормально, но вот что получится в результате макроподстановки:

В итоге переменная max будет равна не 4, как мы ожидали, а 3. Потом можно уйму времени потратить, отлавливая эту ошибку. Так что в качестве аргумента макроса нужно всегда передавать уже конечное значение, а не какое-то выражение или вызов функции. Иначе выражение или функция будут вычислены столько раз, сколько используется этот параметр в теле макроса.

2. Все аргументы макроса и сам макрос должны быть заключены в скобки.

Это правило я уже нарушил при написании макроса MAX. Что получится, если мы захотим использовать этот макрос в составе какого-то математического выражения?

По логике, переменная result должна будет иметь значение 9, однако вот что мы получаем в результате макроподстановки:

И переменная result внезапно примет значение 1. Чтобы такого не происходило, макрос MAX должен быть объявлен следующим образом:

В таком случае все действия произойдут в нужном порядке.

3. Многострочные макросы должны иметь свою область видимости.

Например у нас есть макрос, который вызывает две функции:

А теперь попробуем использовать этот макрос в таком контексте:

После макроподстановки мы увидим вот такую картину:

Поскольку в условии цикла стоит ноль, он отработает ровно один раз. Это делается, во первых, для того, чтобы у тела макроса появилась своя область видимости, ограниченная телом цикла, а во вторых, чтобы сделать вызов макроса более привычным, потому что теперь после MACRO() нужно будет ставить точку с запятой. Если бы мы просто ограничили тело макроса фигурными скобками, точку с запятой после его вызова поставить бы не получилось.

Еще немного примеров

Теперь чтобы нагенерировать таких функций для нужных нам типов, нужно просто использовать пару раз этот макрос в глобальной зоне видимости:

Таким образом у нас получился аналог шаблонов из С++. Но стоит сразу обратить внимание, что данный способ не подойдет для типов, название которых состоит более чем из одного слова, например long long или unsigned short, потому что не получится нормально склеить название функции (sum_##type). Для этого сперва придется объявить для них новый тип, состоящий из одного слова.

В современном С++ можно спокойно обходиться без макросов вовсе, используя только шаблоны и inline-функции. Но в Си жить с макросами все же удобнее, чем без них. При грамотном использовании макросы позволяют избавиться от большого количества дублирования кода и сделать сам код более симпатичным и удобочитаемым.

Источник

Грязные трюки с макросами C++

Несколько полезных ссылок

Для начинающих: статья (на английском) Anders Lindgren — Tips and tricks using the preprocessor (part one), покрывает самые основы макросов.
Для продвинутых: статья (на английском) Anders Lindgren — Tips and tricks using the preprocessor (part two), покрывает более серьезные темы. Кое-что будет и в этой статье, но не все, и с меньшим количеством объяснений.
Для профессионалов: статья (на английском) Aditya Kumar, Andrew Sutton, Bjarne Stroustrup — Rejuvenating C++ Programs through Demacrofication, описывает возможности по замене макросов на фичи C++11.

Читайте также:  метформин таблетки для чего применяется

Небольшое культурное различие

Согласно Википедии и моим собственным ощущениям, в русском языке мы обычно понимаем под словом «макрос» вот это:А следующее:
у нас называется «константой препроцессора» (или попросту «дефайн»’ом). В английском языке немного не так: первое называется function-like macro, а второе — object-like macro (опять же, приведу ссылку на Википедию). То есть, когда они говорят о макросах, они могут иметь в виду как одно, так и другое, так и все вместе. Будьте внимательны при чтении английских текстов.

Что такое хорошо и что такое плохо

Так что, правильнее будет сказать, что «макросы сложно отлаживать». Но, тем не менее, проблема с отладкой макросов существует.

Чтобы определить, нуждается ли используемый Вами макрос в отладке, подумайте, есть ли в нем то, ради чего стоит захотеть запихнуть туда точку останова. Это может быть изменение значений, полученных через параметры, объявление переменных, изменение объектов или данных снаружи и тому подобное.

Чтобы показать, о каких побочных эффектах идет речь, обычно приводят пример с арифметическими операциями. Я тоже не стану отступать от этой традиции:
В выводе ожидаем 4 и 12, а получаем 4 и 8. Дело в том, что макрос просто подставляет код туда, куда указано. И в данном случае код будет выглядеть так:
Это и есть побочный эффект. Чтобы все заработало, как ожидается, нужно изменить наш макрос:
Теперь верно. Но это еще не все. Перейдем к умножению:
Сразу же запишем его «правильно», но используем чуть иначе:
Дежавю: снова получаем 4 и 8. В данном случае развернутый макрос будет выглядеть как:
То есть, теперь нам нужно написать:
Используем эту версию макроса и вуаля:
Теперь все правильно.

Эта проблема усугубляется тем, что далеко не все типы параметров можно обернуть в скобки (реальный пример будет дальше в статье). Из-за этого сделать качественные макросы бывает довольно сложно.

Кроме того, как напомнил encyclopedist в комментариях, бывают случаи, когда и скобки не спасают:

В пункте про побочные эффекты вы ещё забыли упомянуть частую проблему — макросы могут вычислять свои аргументы несколько раз. В худшем случае это приводит к странным побочным эффектам, в более легком — к проблемам производительности.
Пример

Безопасный вызов метода

tenzink в комментариях указал на проблему с этими макросами, которую я благополучно не учел при написании статьи:
При таком вызове getObject вызовется дважды.

К сожалению, как показала статья, далеко не каждый программист об этом догадается, поэтому считать эти макросы хорошими я больше не могу. 🙁

Тем не менее, похожие макросы (несколько иначе реализованные) я встречал в реальном продакшн коде, они использовались командой программистов, в том числе и мной. Каких либо проблем из-за них на моей памяти не возникало

Новая версия, появившаяся благодаря lemelisk и C++14:

Обратите внимание на параметр methodWithArguments. Это тот самый пример параметра, который нельзя обернуть скобками. Это значит, что кроме вызова метода в параметр можно запихнуть и что-нибудь еще. Тем не менее, случайно это устроить довольно проблематично, поэтому я не считаю эти макросы «плохими».

Кроме этого, теперь у нас добавился overhead на вызов лямбды. Теоретически, можно предположить, что лямбда, вызываемая там же, где она определена, будет заинлайнена. Но подтверждения этому в сети я не нашел, так что лучше всего будет проверить это «вручную» для Вашего компилятора.

Как эти два макроса используются, думаю, понятно. Если имеем код:
то с помощью макроса safeCallVoid он превращается в:
и, аналогично, для случая с возвращаемым значением:

Для чего? В первую очередь, эти макросы позволяют увеличить читаемость кода, уменьшить вложенность. Наибольший положительный эффект дают в совокупности с небольшими методами (то есть, если следовать принципам рефакторинга).

Неиспользуемые переменные

Подобный макрос есть, например, в cocos2d-x, там он называется CC_UNUSED_PARAM. Из недостатков: теоретически, он может работать не на всех компиляторах. Тем не менее, в cocos2d-x он для всех платформ определен абсолютно одинаково.

Для чего? Этот макрос позволяет избежать предупреждения о неиспользуемой переменной, а читающему код он как бы говорит: «тот кто писал это — знал, что переменная не используется, все в порядке».

Превращение в строку

Да, вот так вот сурово, сразу в std::string. Плюсы и минусы использования строкового класса оставим за рамками разговора, поговорим только о макросе.

Использовать его можно так:
И еще так:
И даже так:
Однако, в последнем примере перенос строки будет заменен на пробел. Для реального переноса нужно использовать ‘\n’:
Также, можно использовать и другие символы, например ‘\’ для конкатенации строк, ‘\t’ и прочие.

Для чего? Может использоваться для упрощения вывода отладочной информации или, например, для создания фабрики объектов с текстовыми id (в этом случае, такой макрос может использоваться при регистрации класса в фабрике для превращения имени класса в строку).

Запятая в параметре макроса

Для чего? Используется при необходимости передать в другой макрос аргумент, содержащий запятые, как один аргумент и невозможности использовать для этого скобки.

Бесконечный цикл

Для чего? Когда while(true), while(1), for(;;) и прочие стандартные пути создания цикла кажутся не слишком информативными, можно использовать подобный макрос. Едиственный плюс который он дает — чуть лучшую читаемость кода.

Заключение

При правильном использовании макросы вовсе не являются чем-то плохим. Главное, не злоупотреблять ими и следовать нехитрым правилам по созданию «хороших» макросов. И тогда они станут Вашими лучшими помощниками.

Upd. Вернул в статью «Безопасный вызов метода», спасибо lemelisk за подсказку с лямбдами.

Источник

Макросы в Си: как, когда и зачем?

Программисты Си, дойдя до определённого уровня квалификации, обязательно сталкиваются с одной из особенностей этого языка — макросами. Почти во всех других языках аналога макросов нет. И это неспроста. Использование макросов может оказаться весьма небезопасным. В них скрывается ряд особенностей, специфика которых не всегда лежит на поверхности.

Прим. перев. Макросы в таких языках, как Lisp, Scala и Rust, во многом лишены тех проблем и недостатков, которые описаны в этой статье.

Макросы и функции

При первом знакомстве макросы могут показаться обычными вызовами функций. Конечно, у них немного странный синтаксис, но они «ведут себя» как обычные функции. Тогда в чём разница?

Макрос можно условно назвать функцией обработки и замены программного кода: после сборки программы макросы заменяются макроопределениями. Вот как это выглядит:

Этот код преобразуется в следующий:

При вызове же функции под неё выделяется новый стековый кадр, и она выполняется самостоятельно и не зависит от места в коде, откуда была вызвана. Таким образом, переменные с одинаковыми именами в разных функциях не вызовут ошибку, даже если вызывать одну функцию из другой.

Читайте также:  мелкая морковь чем подкормить в августе

Но если попытаться проделать такой же трюк с помощью макроса, то во время компиляции будет выброшена ошибка, так как обе переменные в итоге будут находиться в одной функции:

Один из способов решить эту проблему — поместить тело макроса в новую область видимости имён:

Простым решением было бы не ставить после использования такого макроса точку с запятой, но тогда программисту потребовалось бы помнить о необходимости ставить или не ставить точку с запятой для каждого макроса.

18–19 сентября, Онлайн, Беcплатно

Обычно эту проблему решают с помощью такого трюка:

Такой цикл выполнится только один раз, но поскольку конструкция do-while в Си требует точки с запятой после условия, стоящая после макроса точка с запятой будет отнесена к нему, а не воспринята как отдельная команда.

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

И, наконец, макросы не подлежат отладке. В отладчике можно войти в функцию и пройтись по её коду, а вот с макросами такое не пройдёт. Поэтому, если макрос почему-то сбоит, единственный способ выявить проблему — переходить к его определению и разбираться уже там.

Тем не менее, можно выделить одно явное преимущество макросов перед функциями — производительность. Макрос быстрее, чем функция. Как уже упоминалось выше, под функцию выделяются дополнительные ресурсы, которые можно сэкономить, если использовать макросы. Это преимущество может сыграть весомую роль в системах с ограниченными ресурсами (например, в очень старых микроконтроллерах). Но даже в современных системах программисты производят оптимизации, используя макросы для небольших процедур.

Прим. перев. Интересный подход к оптимизации использования ресурсов в программе на Си рассмотрен в другой нашей статье.

В C99 и C++ существует альтернатива макросам — встраиваемые (inline) функции. Если добавить ключевое слово inline перед функцией, компилятору будет дано указание включить тело функции в место её вызова (по аналогии с макросом). При этом встраиваемые функции могут быть отлажены, и у них есть проверка типов.

Встраиваемые функции — отличная штука, которая, как может показаться, делает использование макросов нецелесообразным. Однако это не совсем так.

Когда использовать макросы в Cи

Передача аргументов по умолчанию

В C++ есть весьма удобный инструмент, которого нет в Си, — аргументы по умолчанию:

В Си задача опциональных аргументов может быть решена с помощью макросов:

Этот код довольно безопасен и не скрывает никаких подводных камней (их мы обсудим далее). Конечно, эту задачу можно также решить с помощью функции, но она достаточно тривиальна, чтобы нести накладные расходы при использовании функций. Макрос справляется с ней на ура.

Использование отладочных строк

Их можно включать в определения макросов, используемых для отладки:

В итоге получается интересный способ ведения логов.

Модификация синтаксиса

Это очень мощная особенность макросов. Используя её, можно создать свой собственный синтаксис.

В этой вставке кода определена структура, содержащая связный список. Предполагая, что его узлы заполнены, можно пройтись по нему с помощью LIST_FOREACH так же, как и при использовании foreach в современных языках.

Эта техника действительно очень эффективна и при правильном использовании может дать довольно хорошие результаты.

Другие типы макросов

Помимо макросов, которые подменяют функции, есть и другие очень полезные директивы препроцессора. Вот некоторые из наиболее часто используемых:

#ifdef играет ключевую роль при создании заголовочных файлов. Использование этого макроса гарантирует, что заголовочный файл включён только один раз:

Стоит отметить, что основное предназначение #ifdef — условная компиляция блоков кода на основе некоторого условия. Например, вывод отладочной информации только в режиме отладки:

Перечисления в то же время — полноценные константы. Они могут использоваться в операторах switch-case и для определения размера массива. Однако их недостаток заключается в том, что в перечислениях можно использовать только целые числа. Их нельзя использовать для строковых констант и констант с плавающей запятой.
Вот почему использование #define — оптимальный вариант, если нужно достичь единообразия в определении различного рода констант.

Таким образом, у макросов есть свои преимущества. Но использовать их нужно весьма аккуратно.

Подводные камни при использовании макросов

Отсутствие скобок

Наиболее распространённая ошибка, которую допускают при использовании макросов, — отсутствие скобок вокруг аргументов в определениях макросов. Поскольку макросы подставляются непосредственно в код, это может вызвать неприятные побочные эффекты:

В этом примере выполняется вычисление MULTIPLY(x + 5) и ожидаемый результат — 50. Но в процессе подстановки произойдёт следующее преобразование:

Как несложно подсчитать, данное выражение выдаст не 50, а 30.

А вот как выполнить данную задачу правильно:

Инкремент и декремент

Допустим, есть такой код:

Здесь можно ожидать, что x будет увеличен на единицу и будет равен 6, а результат — 5. Но вот что получится в реальной жизни:

Как видно, x увеличивается на единицу в первый раз при проверке и во второй раз при определении результата, что и приводит к соответствующим итогам.

Передача вызовов функций

Использованием функции в коде никого не удивишь. Равно как и передачей результата одной функции в виде аргумента для другой. Часто это делается так:

И в этой вставке кода всё в порядке. Но, когда это же производится с помощью макроса, можно столкнуться с серьёзными проблемами производительности. Допустим, есть вот этот код:

Многострочные макросы

Программисты не всегда используют фигурные скобки вокруг единичных команд в циклах и условных операторах. Но, если произвести макроподстановку внутри этого блока, и этот макрос будет содержать несколько строк, это приведёт к весьма интересным результатам:

Во вставке кода выше нет фигурных скобок в первом цикле и, так как макрос заменяет одну строку несколькими, только первое выражение в макросе выполняется в цикле. Следовательно, это приведёт к бесконечному циклу, так как i никогда не увеличится.

Именно из-за таких особенностей многие стараются избегать использования макросов.

Хорошая практика

Чтобы свести к минимуму проблемы, вызванные использованием макросов, хорошей практикой будет использование единого подхода для определения макросов в вашем коде. Каким будет этот подход, не имеет значения. Есть проекты, в которых все макроопределения объявлены в верхнем регистре. В некоторых проектах в начале имени макроса используют букву «m». Выберите себе любой подход, но этот подход должен быть таким, чтобы и вы, и другой программист, который будет работать с вашим кодом, сразу понимал, что имеет дело с макросами.

Прим. перев. Другие полезные практики оформления кода можно посмотреть в нашей статье.

Вывод

В большинстве случаев программисты предпочитают использовать функции, поскольку макросы скрывают некоторые серьёзные побочные эффекты. Тем не менее, использование макросов иногда более целесообразно. В этом случае нужно соблюдать правила их именования и учитывать специфику их использования, рассмотренную в статье.

Источник

Образовательный портал