HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-03-10
1 <p>Когда C-разработчик достигает определенного уровня квалификации, он обязательно сталкиваются с<strong>макросами</strong>. Во многих других языках программирования аналога макросов нет, причем неспроста, ведь их применение может оказаться небезопасным. Все дело в том, что макросы в Си имеют ряд особенностей и нюансов, которые не всегда понятны на первый взгляд.</p>
1 <p>Когда C-разработчик достигает определенного уровня квалификации, он обязательно сталкиваются с<strong>макросами</strong>. Во многих других языках программирования аналога макросов нет, причем неспроста, ведь их применение может оказаться небезопасным. Все дело в том, что макросы в Си имеют ряд особенностей и нюансов, которые не всегда понятны на первый взгляд.</p>
2 <p>Когда программист только знакомится с макросами, они могут показаться ему самыми, что ни на есть обычными<strong>вызовами функций</strong>. Да, с немного странным синтаксисом, но все-таки функциями, да и ведут они себя, как функции. Так в чем же разница?</p>
2 <p>Когда программист только знакомится с макросами, они могут показаться ему самыми, что ни на есть обычными<strong>вызовами функций</strong>. Да, с немного странным синтаксисом, но все-таки функциями, да и ведут они себя, как функции. Так в чем же разница?</p>
3 <p>Если говорить условно, то макрос - это функция обработки и замены программного кода. После того, как будет выполнена сборка программы, макросы меняются на макроопределения. Как это выглядит, лучше рассмотреть на примере:</p>
3 <p>Если говорить условно, то макрос - это функция обработки и замены программного кода. После того, как будет выполнена сборка программы, макросы меняются на макроопределения. Как это выглядит, лучше рассмотреть на примере:</p>
4 <p>При этом данный код преобразуется в другой (отработавший код опустим):</p>
4 <p>При этом данный код преобразуется в другой (отработавший код опустим):</p>
5 <p>В процессе вызова функции, под нее выделяется новый<strong>стековый кадр</strong>, и функция выполняется самостоятельно, то есть она не зависит от места в программном коде, откуда была вызвана. В результате переменные с одинаковыми именами в различных функциях ошибку не вызовут даже в том случае, если вызывать лишь одну функцию из другой.</p>
5 <p>В процессе вызова функции, под нее выделяется новый<strong>стековый кадр</strong>, и функция выполняется самостоятельно, то есть она не зависит от места в программном коде, откуда была вызвана. В результате переменные с одинаковыми именами в различных функциях ошибку не вызовут даже в том случае, если вызывать лишь одну функцию из другой.</p>
6 <p>Однако если попытаться выполнить тот же трюк с помощью макроса, то в процессе компиляции будет выброшена ошибка. Дело в том, что обе переменные по итогу станут находиться в одной функции:</p>
6 <p>Однако если попытаться выполнить тот же трюк с помощью макроса, то в процессе компиляции будет выброшена ошибка. Дело в том, что обе переменные по итогу станут находиться в одной функции:</p>
7 <p>Мало того, функции ведь выполняют проверку типов: если функция ожидает строку на входе, но получает число, то будет выброшена ошибка (либо, в крайнем случае, предупреждение, что уже зависит от компилятора). Макросы же просто меняют аргумент, который им передан.</p>
7 <p>Мало того, функции ведь выполняют проверку типов: если функция ожидает строку на входе, но получает число, то будет выброшена ошибка (либо, в крайнем случае, предупреждение, что уже зависит от компилятора). Макросы же просто меняют аргумент, который им передан.</p>
8 <p>Вдобавок к этому, макросы не подлежат отладке. К примеру, в отладчике мы можем войти в функцию и пройтись по программному коду функции, а вот с макросами этот номер не пройдет. В результате если макрос по каким-то причинам сбоит, то существует лишь единственный способ обнаружить проблему - перейти к определению макроса, разбираясь уже там.</p>
8 <p>Вдобавок к этому, макросы не подлежат отладке. К примеру, в отладчике мы можем войти в функцию и пройтись по программному коду функции, а вот с макросами этот номер не пройдет. В результате если макрос по каким-то причинам сбоит, то существует лишь единственный способ обнаружить проблему - перейти к определению макроса, разбираясь уже там.</p>
9 <p>Однако нельзя не выделить явное преимущество макросов перед функциями - это производительность.<strong>Макрос быстрее функции</strong>. Мы уже упоминали, что под функцию выделяются допресурсы. Используя макросы, эти ресурсы можно сэкономить. Данный плюс порой играет значительную роль, особенно когда речь идет о системах, имеющих ограниченные ресурсы (достаточно вспомнить очень старые микроконтроллеры). При этом даже в современных системах разработчики выполняют оптимизацию, применяя для небольших процедур макросы.</p>
9 <p>Однако нельзя не выделить явное преимущество макросов перед функциями - это производительность.<strong>Макрос быстрее функции</strong>. Мы уже упоминали, что под функцию выделяются допресурсы. Используя макросы, эти ресурсы можно сэкономить. Данный плюс порой играет значительную роль, особенно когда речь идет о системах, имеющих ограниченные ресурсы (достаточно вспомнить очень старые микроконтроллеры). При этом даже в современных системах разработчики выполняют оптимизацию, применяя для небольших процедур макросы.</p>
10 <p>Однако в C99 и C++ есть альтернатива макросам - встраиваемые (<strong>inline</strong>) функции. Если добавить перед функцией ключевое слово<strong>inline</strong>, компилятор получит указание включить тело функции непосредственно в место вызова функции (как и в случае с макросом). Причем встраиваемые функции могут быть отлажены, плюс у них существует проверка типов.</p>
10 <p>Однако в C99 и C++ есть альтернатива макросам - встраиваемые (<strong>inline</strong>) функции. Если добавить перед функцией ключевое слово<strong>inline</strong>, компилятор получит указание включить тело функции непосредственно в место вызова функции (как и в случае с макросом). Причем встраиваемые функции могут быть отлажены, плюс у них существует проверка типов.</p>
11 <p>Но ключевое слово inline - это лишь подсказка для компилятора, то есть это не строгое правило, в результате чего компилятор может и проигнорировать данную подсказку. Дабы так не случилось, в<em>gcc</em>существует атрибут<em>always_inline</em>, заставляющий компилятор встраивать функцию. Конечно, встраиваемые функции - вещь неплохая, поэтому может возникнуть мысль, что применение макросов становится нецелесообразным. Но на деле это не совсем так. Однако о том, как лучше использовать макросы в Си, мы поговорим в следующий раз, поэтому следите за новостями!</p>
11 <p>Но ключевое слово inline - это лишь подсказка для компилятора, то есть это не строгое правило, в результате чего компилятор может и проигнорировать данную подсказку. Дабы так не случилось, в<em>gcc</em>существует атрибут<em>always_inline</em>, заставляющий компилятор встраивать функцию. Конечно, встраиваемые функции - вещь неплохая, поэтому может возникнуть мысль, что применение макросов становится нецелесообразным. Но на деле это не совсем так. Однако о том, как лучше использовать макросы в Си, мы поговорим в следующий раз, поэтому следите за новостями!</p>
12 <p><em>По материалам статьи "<a>How to properly use macros in C</a>"</em></p>
12 <p><em>По материалам статьи "<a>How to properly use macros in C</a>"</em></p>
13  
13