Macros в C є потужним рисою фази попереднього обробки, що дозволяє програмістам визначати константи, внутріювати коди, і виконувати умовну компіляцію. При використанні ефективно вони значно підвищують здатність коду і підтримуваність, дозволяють розробникам писати очищувач, більш гнучкі програми. Однак їх потужність поставляється з ризиками; неправильне використання макросу може ввести тонкі помилки і зробити код важче для знеболювання. Ця стаття досліджує, як загартувати макроси для максимальної вигоди при уникненні поширених підводних каменів, з кращими практиками, що мальовані з досвіду галузі і стандарту C.

Що таке Макрос?

Macros визначаються за допомогою прямої та обробляються препроцесором C перед початком складання. Передпроцесор виконує простий текстовий заміщення: кожен випадок макросиму імені в коді джерела замінюється макросислим визначенням. Ця заміна є чисто текстовим і не поважає обсяг або контроль типу. Macros може бути object-like (використаний для констанцій) або function-like (використаний для код фрагментів, які приймають аргументи).

#define PI 3.14159 // object-like macro
#define SQUARE(x) ((x)*(x)) // function-like macro

Розуміння цього кроку попереднього обробки є вирішальним, оскільки макроси не дотримуються однакових правил, як функцій або змінних. Вони розширюються в режимі онлайн, що дає їм переваги продуктивності, але також представляє унікальні виклики.

Переваги Macros для зручності та обслуговування коду

При нанесенні думливо макроси забезпечують кілька бетонних переваг у великих або експлуатаційних базах:

  • Code Reusability: Визначте загальний розрахунок або перехресний раз і використовуйте його по всій програмі. Зміна макроновлення кожного екземпляра автоматично.
  • Elimination of Magic Numbers: Замість розсіювання літрів, таких як або , дати їм значущі імена. Це покращує читабельність і робить параметри тюнінгу тривіально.
  • Перформанс Gains]: Функціональні макроси, які не дозволяють накладу функції виклику. Для малих, часто використовуваних операцій (наприклад, хв/макс, квадрат), макроси можуть бути швидше, ніж навіть в лінію функції в певних компіляторах, де нахил не гарантується.
  • Conditional Збірка: Macros працює вручну з , , і , щоб включати або виключити код на основі компіляції часових умов. Це важливо для платформи специфічного коду, дебвуг збірки або функцій, щоб окуляри.
  • Спрощена конфігурація: Визначаючи макроси з різними значеннями в одному заголовку, можна адаптувати поведінку всієї бази коду без дотику виконання файлів.

Письмові робуст Макрос: кращі практики

Гнучкість макросів також є їх небезпекою. Погано письмовий макрос може викликати несподівану поведінку, яка важко слідувати. Дотримуйтесь цих кращих практик, щоб зберегти ваші макроси безпечними та безпечними.

Завжди параметри розміру Parenthesize і експресія Entire

Оскільки макроси є текстовими замінами, операторська прецедентія може зламати їх. макросхожі будуть розширюватися до (який дорівнює 11, не 25). Фіксація полягає в тому, щоб закривати кожен параметр у дужках і обмотувати весь вираз:

#define SQUARE(x) ((x) * (x)) // now SQUARE(3+2) works correctly

Уникайте декількох оцінок аргументів

Макро аргументи замінюються як-is. Якщо аргумент має побічні ефекти (наприклад, ), він може бути оцінений більше одного разу. Наприклад:

#define MAX(a,b) ((a) > (b) ? (a) : (b))
int x = 5;
int max = MAX(x++, 10); // expands to ((x++) > (10) ? (x++) : (10)) // x incremented twice!

Щоб уникнути цього, віддайте перевагу в режимі онлайн або використовуйте обережні конвенції для сповіщення користувачів. Якщо ви повинні використовувати макроси, документ, який аргументує не містити побічних ефектів.

Використовуйте верхній чохол для імен Macro

За конвенцією макроси написані у верхньому літері (наприклад, , ]). Це робить їх виділятися з змінних та функцій, зменшуючи шанс випадкового зіткнення.

End Macros з Do-While для багатосторонніх блоків

При розшифровці макросу, що містить кілька виписок, загортають їх в ] петлі. Це забезпечує макросхабани правильно в усіх контекстах (наприклад, після без брекетів):

#define LOG_ERROR(msg) do { \
 fprintf(stderr, "Error: %s\n", msg); \
 exit(1); \
} while (0)

Документ Макрос Торубо

Macros не вистачає самодогляду природи функцій. Завжди додають коментарі, що описують аргументи, очікувану поведінку та будь-які обмеження. Використовуйте коментарі Doxygen-style при відповідному виконанні.

Розширені методи Macro

Для цього, як і раніше, ми можемо зробити ваші макроси, але більш гнучкі.

оператор перетворює макросиг в рядок літра. Це корисно для розлучення або залогування:

#define PRINT_VAR(x) printf(#x " = %d\n", x)
int value = 42;
PRINT_VAR(value); // prints: value = 42

Токен Пастинг з

оператор погоджується два токени в один токени. Це дозволяє створювати ідентифікатори динамічно в умовах компіляції:

#define CREATE_VAR(prefix, index) prefix ## index
int CREATE_VAR(arr, 0) = 10; // becomes int arr0 = 10;

Паста Token широко використовується в макросах X і для створення повторюваних шаблонів коду.

Марди Гра (C99)

Введений в C99, variadic макроси приймають змінну кількість аргументів за допомогою . Це дозволяє гнучкі залогові та дебулінгові макроси:

#define DEBUG_PRINT(fmt, ...) \
 fprintf(stderr, "DEBUG: " fmt, __VA_ARGS__)
DEBUG_PRINT("x = %d, y = %d", x, y);

У C11 і пізніше ви можете використовувати для обробки причепів коми в порожніх аргументах, поліпшення переносності.

Загальні Питви та Як уникнути

Навіть досвідчені розробники C зустрічаються макро пов'язані помилки. Визначте ці підводні камені рано запобігає головного болю.

  • ]Поцілунки напівколони або брекети]: макросир, який розширює блок без причіпного напівколону може викликати помилки синтаксису. шаблон вирішує це.
  • Попередня думка дива : Завжди дужина — як зазначено вище.
  • Подрібнювач макросів з функціями: Якщо ви визначаєте макроси з такою назвою, як стандартна функція бібліотеки, вона може мовчати її надвисати. Використовуйте унікальний нам або #undef.
  • Non-constant initializers: Об'єктно-подібні макроси використовуються як постійні, але вони не типовані. Вони можуть бути використані в постійні вирази тільки якщо сама розширення є постійним виразом.
  • ]Вилучення складності: Macros розширюються до компілятора бачить їх, тому повідомлення про помилку відносяться до розширеного коду, а не макрозв'язку. Використання прапора з GCC або Clang (наприклад, ]) показує попередньо оброблений вихід для слідів.

Альтернативи макрос: конс, енум, Інлайн функції

Сучасні C надає безпечні альтернативи, які часто усувають необхідність макросів:

  • змінні]: Для нумеричних констанцій, віддають перевагу . Вони мають належне визначення типу та повагу. Однак вони не компілюють часові постійні стани в усіх контекстах (наприклад, розміри масиву в C90). У C99 і пізніше ] може бути використаний для масивних розмірів, якщо змінна дійсно стійка (наприклад, ).
  • : Для набору суміжних цілих констанцій, енумів є тип-безпечними і автоматично призначає значення. Вони широко використовуються для кодів помилок і прапорів.
  • Inline функції]: Вступ в C99, функції забезпечують продуктивність макросу з повним захистом типу, підтримка відключення та уникнення побічних ефектів. Наприклад:
static inline int square(int x) {
 return x * x;
}

Використовуйте вбудовані функції, коли вам потрібно функцію-подібний макрос, який оцінює аргументи точно один раз. Єдиний випадок використання функцій, як макрос, коли потрібно працювати на типах (наприклад, тип у GNU C) або потрібно можливість відкрити новий обсяг (наприклад, do-while idiom).

Приклади реального світу: видалення та налаштування

Macros excel в умовних сценаріях. Тут є два практичних застосування:

Дебюг Локування з керуванням Compile-Time

#ifdef DEBUG
 #define LOG(fmt, ...) fprintf(stderr, "[DEBUG] " fmt, ##__VA_ARGS__)
#else
 #define LOG(fmt, ...) /* no-op */
#endif

Цей макрологи тільки при скомпільовані , і порожні макроси викликає нульовий наклад у випускних зборах. перед ] - розширення GNU, яке знімає причіпний кома, коли не передбачені аргументи (також підтримується в C20 з ).

Платформа-Спеціальний код

#if defined(_WIN32) || defined(_WIN64)
 #define PLATFORM "Windows"
#elif defined(__linux__)
 #define PLATFORM "Linux"
#else
 #define PLATFORM "Unknown"
#endif

Цей шаблон ізолює залежності платформи без піддавання всіх функцій. Комбінований з охоронцями, він зберігає заголовки чистими та безпечними.

Висновок

Macros залишаються незамінним інструментом для відновлення коду та обслуговування, особливо в умовах виконання-критичних або вбудованих контекстів, де кожен цикл рахує. При використанні правильно - при правильній батьківських заготовок, обмежених побічних ефектів, а також відповідних намах - зменшення дублювання та налаштування централізованого використання. Однак тенденція в сучасному C полягає в тому, щоб замінити функціональні макроси з вбудованими функціями або генеринами (на основі в C11), щоб отримати безпеку та небажаність. Для об'єктів-подібних макросів і часто перевершують.

За допомогою балансування традиційної макросильни з цими альтернативними варіантами Safer можна писати C-код, який є одночасно ефективним і надійним. Для подальшого читання консультуйтеся з GNU C Preprocessor Manual і C посилання на макроси. Розуміння макроводів докладно також покрито embedded.com статті.