Макросы vs функции в Си | OTUS

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

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

Когда программист только знакомится с макросами, они могут показаться ему самыми, что ни на есть обычными вызовами функций. Да, с немного странным синтаксисом, но все-таки функциями, да и ведут они себя, как функции. Так в чем же разница?

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

Screenshot_1-1801-91ecc3.png

При этом данный код преобразуется в другой (отработавший код опустим):

Screenshot_2-1801-a20636.png

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

Screenshot_3-1801-746981.png

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

Screenshot_4-1801-e59f62.png Screenshot_5-1801-aaaf87.png

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

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

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

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

Но ключевое слово inline — это лишь подсказка для компилятора, то есть это не строгое правило, в результате чего компилятор может и проигнорировать данную подсказку. Дабы так не случилось, в gcc существует атрибут always_inline, заставляющий компилятор встраивать функцию. Конечно, встраиваемые функции — вещь неплохая, поэтому может возникнуть мысль, что применение макросов становится нецелесообразным. Но на деле это не совсем так. Однако о том, как лучше использовать макросы в Си, мы поговорим в следующий раз, поэтому следите за новостями!

По материалам статьи «How to properly use macros in C»

Не пропустите новые полезные статьи!

Спасибо за подписку!

Мы отправили вам письмо для подтверждения вашего email.
С уважением, OTUS!

Автор
0 комментариев
Для комментирования необходимо авторизоваться
Популярное
Сегодня тут пусто
Запланируй обучение с выгодой!
Получи скидку 10% на все курсы ноября и декабря до 17.11 →