Макрос - это имя, которое препроцессор заменяет некоторым текстом ещё до запуска компилятора. В этом весь механизм, и любое правило на этой странице - его следствие: макросы не имеют типов, не уважают область видимости, не вычисляют аргументы и понятия не имеют, что такое вызов функции. Они копируют текст.
При аккуратном использовании они незаменимы: переключатели переносимости, макросы-утверждения, захватывающие номер строки, константы времени компиляции, которые годятся для размеров массивов. При небрежном - они порождают баги, которые выглядят невозможными, пока вы не развернёте файл.
Объектные макросы
Простейшая форма задаёт имя, обозначающее значение:
По соглашению такие имена пишут в UPPER_SNAKE_CASE, чтобы читатель с первого взгляда понимал: это макрос, а не переменная. Здесь соглашение важнее обычного, потому что макросы игнорируют область видимости: #define внутри функции всё равно действует до конца файла и с удовольствием перепишет одноимённую переменную в другой функции.
#undef NAME удаляет определение, так что имя можно переопределить позже без предупреждения о переопределении.
Функциональные макросы
Поставьте список параметров сразу после имени - без пробела перед открывающей скобкой - и макрос начнёт принимать аргументы:
Правило про пробел реально: #define SQUARE (x) ((x)*(x)) определяет объектный макрос по имени SQUARE, чей текст замены начинается с (x). В последующей ошибке компилятора про пробел не будет ни слова.
Правила скобок
Их два, и оба существуют потому, что тело вставляется в выражение, которого вы не видите, когда пишете макрос.
Правило 1: заключайте в скобки каждый параметр. Без этого аргумент, который сам является выражением, разрывается приоритетами операций:
#define SQUARE_BAD(x) x * x
SQUARE_BAD(2 + 3) /* становится 2 + 3 * 2 + 3 == 11, а не 25 */
Правило 2: заключайте в скобки всё тело. Без этого окружающее выражение разрывает уже результат:
#define DOUBLE_BAD(x) (x) + (x)
10 / DOUBLE_BAD(5) /* становится 10 / (5) + (5) == 7, а не 1 */
Запустите обе поломки рядом с исправленными версиями:
Обе плохие версии компилируются без единого предупреждения и выдают неверные числа. Привычку нужно выработать механическую: оберните каждый параметр, затем оберните результат. ((a) > (b) ? (a) : (b)) выглядит шумно, и этот шум здесь как раз по делу.
Ловушка многократного вычисления
От второй опасности скобки не спасут. Макрос вставляет текст аргумента в каждое место, где встречается параметр, поэтому аргумент с побочным эффектом срабатывает не один раз:
Макрос разворачивается в ((i++) > (j) ? (i++) : (j)). Сравнение увеличивает i до 6 и сравнивает 5 с 3; истинная ветка снова увеличивает i, до 7, и выдаёт 6. Итого m равно 6, а i равно 7 - ни то, ни другое не похоже на «большее из i и j». Функция max(i++, j) сработала бы верно, потому что аргумент вычисляется один раз до вызова.
Та же ловушка ловит всё, что имеет побочный эффект: SQUARE(rand()) вызывает rand() дважды и перемножает два разных числа. CHECK(read_byte()) съедает два байта.
Две линии защиты, в порядке предпочтения:
- Используйте настоящую функцию. Если она нужна для нескольких типов, напишите по одной на тип или возьмите
static inlineфункцию в заголовке. - Если это обязан быть макрос, громко задокументируйте, что аргументы вычисляются больше одного раза, и держите места вызова свободными от побочных эффектов.
Многострочные макросы и do-while(0)
Тело макроса может занимать несколько строк, если каждая строка, кроме последней, заканчивается обратным слэшем. Наивная версия выглядит нормально:
#define LOG_TWICE(msg) \
printf("%s\n", msg); \
printf("%s\n", msg)
А потом она молча ломается ровно в том единственном месте, где это важно:
if (verbose)
LOG_TWICE("hello");
else
printf("quiet\n");
После подстановки if владеет только первым printf, второй выполняется безусловно, а у else больше нет парного if - ошибка компиляции, чьё сообщение указывает в никуда. Обернуть тело в обычные фигурные скобки не лучше: завершающая ; после LOG_TWICE("hello") становится пустым оператором, который завершает if, и else ломается снова.
Работающая идиома - do { ... } while (0):
do { ... } while (0) - это один оператор, он выполняет своё тело ровно один раз и требует точку с запятой после себя, поэтому место вызова читается как обычный вызов функции и ведёт себя так же в любом контексте управления потоком. Следите за обратными слэшами: случайный пробел после слэша обрывает продолжение строки и порождает совершенно непонятную ошибку.
Предопределённые макросы и шаблоны утверждений
Некоторые макросы препроцессор определяет сам, и именно из-за них кое-что просто не может быть функцией:
__FILE__ и __LINE__ разворачиваются там, где написаны, поэтому внутри макроса они захватывают позицию вызывающего кода. Функция так не умеет - внутри функции они всегда сообщали бы файл и строку самой функции логирования. Ровно так стандартный макрос assert сообщает, где именно не выполнилось условие.
Другие полезные предопределённые имена: __DATE__, __TIME__ и __STDC_VERSION__ (например, 201710L для C17).
Два оператора, о которых стоит знать
Внутри тела макроса # превращает параметр в строковый литерал («стрингизация»), а ## склеивает токены («склейка токенов»).
SHOW(width * height) превращается в printf("width * height" " = %d\n", (width * height)); - соседние строковые литералы компилятор склеивает, поэтому один вызов печатает и текст выражения, и его значение. Отладочный приём, который стоит запомнить.
## встречается реже и в основном в макросах-генераторах кода: #define MAKE_VAR(n) int var_##n превращает MAKE_VAR(3) в int var_3. Пользуйтесь им умеренно: идентификаторы, собранные препроцессором, нельзя найти поиском по имени, и по такому коду тяжело ориентироваться.
Макросы, функции и const
Берите самый слабый инструмент, который справляется с задачей:
- Переменная
const- уconst double PI = 3.14159;есть тип, она подчиняется области видимости, видна в отладчике и не может быть переопределена посторонним заголовком. Предпочитайте её для значений, используемых во время выполнения. enum-enum { MAX_USERS = 100 };даёт именованную целочисленную константу, которая работает там, где C требует константу времени компиляции, и при этом имеет тип и область видимости. Хороша для целочисленных пределов.- Функция
static inline- проверяется по типам, вычисляет каждый аргумент ровно один раз, а современные компиляторы встраивают её ничуть не хуже макроса. Это правильная замена почти любому функциональному макросу. - Макрос - когда вам нужен
__LINE__, когда штука должна работать раньше, чем появляются типы (размеры массивов в старом коде), когда вы переключаете код условной компиляцией или когда генерируете однообразный код.
Единственное место, где у макросов до сих пор нет конкурентов, - вычисление размера массива, потому что результат обязан быть константой времени компиляции:
У этого макроса есть собственная широко известная оговорка: он верен только для настоящего массива. Передайте указатель - а именно им массив становится, попав в функцию, - и sizeof измерит указатель, уверенно и молча выдав неверный ответ.
Отладка макроса
Когда макрос ведёт себя странно, не вглядывайтесь в него. Разверните его:
gcc -E program.c | tail -30
Подставленный текст сразу скажет, в чём дело: в пропущенной скобке, в двойном вычислении или в имени, столкнувшемся с чем-то ещё. Компилируйте ещё и с -Wall -Wextra - GCC и clang добавляют примечания «in expansion of macro», связывающие строку с ошибкой с определением макроса.
Далее: заголовочные файлы, где #define и #include вместе позволяют одному проекту занимать много файлов исходного кода, не дублируя одни и те же объявления.
Часто задаваемые вопросы
Что такое макрос в C?
Именованный кусок текста, который препроцессор подставляет в исходник до компиляции. #define MAX 100 превращает каждое последующее MAX в 100; #define SQUARE(x) ((x) * (x)) принимает аргументы и подставляет их в шаблон. У макросов нет типов и они не подчиняются правилам области видимости - это замена текста.
Почему макросам в C нужно столько скобок?
Потому что тело вставляется в окружающий код и разбирается уже целиком. #define SQUARE(x) x * x превращает SQUARE(2 + 3) в 2 + 3 * 2 + 3, то есть в 11, а не в 25. Если обернуть в скобки каждый аргумент и всё тело - ((x) * (x)) - подставленный текст сгруппируется так, как вы задумали, независимо от того, что его окружает.
Чем макрос отличается от функции в C?
Функция компилируется один раз, проверяется по типам и вычисляет каждый аргумент ровно один раз. Макрос вставляется в каждое место использования, ничего не проверяет и может вычислить аргумент несколько раз - так что MAX(i++, j) способен увеличить i дважды. Предпочитайте функции (и const/enum для констант); макросы берите для того, чего функции не умеют, например для захвата __LINE__ или для генерации кода.
Почему многострочный макрос заворачивают в do { ... } while (0)?
Чтобы он вёл себя как один оператор. Голое тело { ... } ломается, когда за ним идёт точка с запятой внутри if/else (точка с запятой преждевременно завершает if), а простая последовательность операторов ломается в if без фигурных скобок. do { ... } while (0) - это один оператор, который принимает завершающую точку с запятой и выполняет своё тело ровно один раз.