История вопроса
Макросы с параметрами — важная часть препроцессора C, появились для быстрого внедрения повторяющихся фрагментов кода и упрощения отладки. Они применяются для небольших функций, инлайнинга или оптимизации.
Проблема
Макросы не проверяют типы и не выполняют полноценную подстановку вне простого текстового замещения. Ошибки случаются из-за отсутствия скобок и подмены выражений с побочными эффектами.
Решение
Предусматривать скобки вокруг параметров и определения макросов, избегать побочных эффектов в аргументах и пользоваться inline-функциями для более сложных случаев.
Пример кода:
#define MAX(a, b) ((a) > (b) ? (a) : (b)) int x = 5, y = 10; int z = MAX(x++, y++); // Опасный вызов!
Ключевые особенности:
Заменяет ли макрос код всегда полностью как функция?
Нет! Макрос — лишь текстовая подстановка до компиляции, может вести себя иначе, чем функция, если аргументы — выражения с побочными эффектами.
Можно ли использовать любой вызов (в том числе с ++, --) в качестве параметра макроса?
Это крайне опасно. Побочные эффекты будут происходить несколько раз, если параметр встречается в макросе больше одного раза.
Пример кода:
// Этот вызов увеличит x или y больше чем на 1 MAX(x++, y++)
Как правильно включать скобки в объявлениях макроса?
Окружать и параметры, и выражение внутри макроса скобками, чтобы избежать ошибок ассоциативности при вызове внутри других выражений.
В компании много лет был определен макрос #define SQUARE(x) xx, и его применяли для выражений типа SQUARE(a+1). Возникали неожиданные ошибки: выражение разворачивалось как a+1a+1, что отличается от (a+1)*(a+1).
Плюсы:
Макрос SQUARE был написан с полными скобками: #define SQUARE(x) ((x)*(x)). Его использование стандартизировано и задокументировано.
Плюсы: