Макросы препроцессора C: подстановка текста и типичные ловушки
💻 Информатика · 11 класс
Макрос в языке C — именованный фрагмент, который препроцессор подставляет вместо использования имени. Он обрабатывается до обычного анализа программы компилятором и не обладает все

Макрос в языке C — именованный фрагмент, который препроцессор подставляет вместо использования имени. Он обрабатывается до обычного анализа программы компилятором и не обладает всеми свойствами функции. Поэтому одинаково выглядящие записи вызова макроса и функции могут вести себя по-разному.
Объектный макрос
Пример #define LIMIT 8 задаёт замену имени LIMIT на последовательность токенов 8. После этого запись int a[LIMIT]; будет обработана с числом вместо имени. Точка с запятой в определение числового макроса обычно не включается: она стала бы частью подстановки и могла бы нарушить выражение.
Макрос не является переменной и не занимает память как отдельный объект только благодаря своему объявлению. У него также нет обычного типа переменной. Если задача решается константой или функцией языка, следует рассмотреть такой вариант: он часто лучше проверяется компилятором. Макрос нужен там, где действительно требуется работа препроцессора.
Макрос с параметром
Опасная запись #define SQUARE(x) x*x при использовании SQUARE(1+2) разворачивается в 1+2*1+2. По приоритетам это 5, а не 9. Скобки вокруг параметров и всего результата уменьшают этот риск: #define SQUARE(x) ((x)*(x)).
Но скобки не превращают макрос в функцию. Для SQUARE(i++) выражение увеличения оказывается в подстановке дважды. Это пример того, почему аргументы с побочными эффектами опасны; не нужно экспериментально подбирать «правильное число», поскольку поведение таких выражений зависит от правил вычисления и может быть неопределённым. Передайте заранее вычисленное значение либо используйте функцию.
Как читать ошибку
Мысленно выполните подстановку, сохранив все операторы и скобки. Затем отдельно примените правила языка C к полученному выражению. Такое двухэтапное чтение позволяет понять, где ошибка: в определении макроса или в предположении о порядке вычисления. Не используйте макрос для скрытого изменения нескольких несвязанных переменных.
В небольшом учебном проекте полезно держать макросы короткими, давать им различимые имена и проверять неожиданные аргументы: сумму, отрицательное значение, выражение с вызовом. Это не заменяет понимание семантики, но быстро выявляет наивные определения.
Практика с ответами
1. Почему #define N 4; отличается от #define N 4? Точка с запятой тоже попадёт в замену. 2. Чему равно SQUARE(2+3) для определения без скобок? Выражение 2+3*2+3 равно 11. 3. Устраняет ли версия ((x)*(x)) все риски? Нет: повторное вычисление аргумента сохраняется.
Для закрепления замените такой макрос обычной функцией, принимающей целое число. Объясните, какие проверки выполняет компилятор и сколько раз вычисляется выражение аргумента при вызове. Сравнение должно учитывать смысл, а не только краткость записи.
Связанные материалы
Начала программирования, Что такое алгоритм.
Источники и проверка
GCC: Macros; GCC: Macro Pitfalls.