В чем плюсы и минусы макросов и макрофункций в C
Что проверяют
Понимаете ли вы, что макрос это текстовая подстановка препроцессора, а не функция. Тестировщику такой вопрос задают на проектах на C и во встроенных системах: важно, видите ли вы, какие ошибки макросы прячут от тестов. Следом часто просят найти баг в #define SQR(x) x*x.
Как отвечать
Бывают макросы-константы (#define BUF_SIZE 256) и функциональные макросы с параметрами (#define MAX(a, b) ((a) > (b) ? (a) : (b))). Те и другие раскрываются до компиляции.
Плюсы:
- нет вызова: код подставляется на место, хотя компилятор с оптимизацией так же встраивает
static inlineфункции; - любые типы: один макрос работает и с
int, и сdouble; - то, чего нет у функций:
#превращает аргумент в строку,##склеивает токены,__FILE__и__LINE__дают место вызова. На этом построеныassertи макросы логирования; - условная компиляция:
#ifdefвключает отладочный код или код под конкретную плату.
Минусы:
- нет проверки типов: ошибка всплывает в раскрытом коде, и сообщение компилятора указывает не туда;
- приоритет операций:
SQR(a + 1)без скобок раскрывается вa + 1*a + 1; - двойное вычисление:
MAX(i++, j)может увеличитьiдважды; - нет области видимости: имя действует до
#undefи конфликтует с чужим кодом; - многострочные макросы: без обёртки
do { ... } while (0)ломаются внутриifбез фигурных скобок.
Для тестирования отсюда следует: каждый вариант условной компиляции это отдельная сборка, которую тоже надо прогонять; gcov считает макрос одной строкой и не показывает ветки внутри; раскрытый код смотрят через gcc -E. Альтернативы: static inline функции, enum и const для констант, _Generic из C11 для обобщённого кода.
Где спотыкаются
Называют функциональные макросы типобезопасными. Типобезопасны inline функции, макрос о типах не знает ничего.
Забывают про побочные эффекты аргументов. Тест с простыми значениями проходит, а с i++ поведение другое.
Считают, что макрос всегда быстрее функции. С оптимизацией разницы обычно нет, а функцию проще отлаживать.