Ответ
Макросы — это директивы препроцессора, выполняющие текстовую подстановку в исходном коде до начала компиляции. Определяются с помощью #define.
Типы макросов:
- Макросы-объекты: Простая замена идентификатора на фрагмент кода.
#define BUFFER_SIZE 1024 char buffer[BUFFER_SIZE]; // Препроцессор заменит на char buffer[1024]; - Функциональные макросы: Макросы с параметрами.
#define MAX(a, b) ((a) > (b) ? (a) : (b)) int x = MAX(10, 20); // Заменится на ((10) > (20) ? (10) : (20))
Критические проблемы макросов:
- Отсутствие типизации и проверки области видимости.
- Побочные эффекты: Из-за множественной подстановки аргументы могут вычисляться несколько раз.
#define SQUARE(x) ((x) * (x)) int a = 5; int bad = SQUARE(++a); // Разворачивается в ((++a) * (++a)) -> Неопределенное поведение! - Сложность отладки: Компилятор и отладчик видят код уже после подстановки.
Современные альтернативы в C++:
- Для констант:
const/constexpr.constexpr size_t BufferSize = 1024; // Типобезопасно, вычисляется на этапе компиляции. - Для функций: Встраиваемые функции (
inline), шаблоны,constexpr-функции.template<typename T> inline T max(T a, T b) { return a > b ? a : b; } // Типобезопасно, нет побочных эффектов.
Вывод: В C++ макросы следует использовать с крайней осторожностью, в основном для условной компиляции (#ifdef DEBUG) или include guard'ов. Для всего остального существуют более безопасные языковые средства.
Ответ 18+ 🔞
Ну вот, сидишь ты такой, пишешь на плюсах, и тут бац — макросы. Ёпта, это же как старый дедовский метод, типа как гаечным ключом забивать гвозди. Вроде работает, но потом вся голова в синяках от последствий.
Смотри, макрос — это такая хитрая жопа, которая работает ещё до того, как компилятор даже посмотрит на твой код. Препроцессор берёт и тупо заменяет один текст на другой. Вообще ни о каких типах или областях видимости он не парится.
Бывают они двух видов, проще говоря. Первый — просто замена, как будто ты в блокноте «найти и заменить» сделал.
#define BUFFER_SIZE 1024
char buffer[BUFFER_SIZE]; // Препроцессор тупо напишет тут char buffer[1024];
Второй — уже поинтереснее, с параметрами, типа функция.
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int x = MAX(10, 20); // А тут будет ((10) > (20) ? (10) : (20))
Вроде норм, да? Ан нет! Вот тут-то и начинается пиздец ручной работы. Проблем — овердохуища.
Главная беда — они тупые как пробка. Никакой проверки типов, никакого ума. И самое страшное — побочные эффекты. Смотри, какой классический косяк:
#define SQUARE(x) ((x) * (x))
int a = 5;
int bad = SQUARE(++a); // Чувак, это развернётся в ((++a) * (++a))!
Что получится? А хуй его знает! Неопределённое поведение, ёпта. Переменная a увеличится два раза, и результат может быть каким угодно. Компилятору доверия ебать ноль в таких случаях.
А отладка? Да ты попробуй найди ошибку, когда в отладчике у тебя уже не MAX(a, b), а какая-то хуйня с кучей скобок, которую препроцессор нагенерировал. Волнение ебать, терпения ноль ебать.
Так нахуя они тогда нужны, спросишь? В современном C++ — почти ни на что. Это как архаизм, пережиток прошлого, типа как будто на дворе 2002-й год.
Вот что делать вместо этого:
- Для констант: Выкинь
#defineнахуй и используйconstилиconstexpr.constexpr size_t BufferSize = 1024; // Всё типобезопасно, всё красиво. - Для функций: Забудь про макросы! Есть
inline-функции, шаблоны,constexpr-функции.template<typename T> inline T max(T a, T b) { return a > b ? a : b; } // Никаких сюрпризов, всё честно.
Итог, блядь: Макросы в C++ — это как динамит. Можно использовать для двух вещей: для условной компиляции (типа #ifdef DEBUG) и для этих самых include guard'ов в хедерах. Всё. Для всего остального есть нормальные, адекватные инструменты языка. Не будь распиздяем, не плоди говнокод.