Директива #define для оголошення макросів має дві синтаксичні форми:
#define ідентифікатор макроозначення
#define ідентифікатор (список параметрів) макроозначення
Ця директива замінює в програмі всі подальші входження ідентифікатора на текст макроозначення. Такий процес називається макропідстановкою. Макроозначення може бути будь-яким фрагментом програми на C++, а також може й бути відсутнім. В останньому випадку всі екземпляри ідентифікатора видаляються з програми.
Розглянута раніше директива препроцесора #define, що оголошує іменований літерал, синтаксично нічим не відрізняється від форми оголошення макросу без параметрів і для препроцесора є її окремим випадком. Наприклад:
#define WIDTH 80 #define LENGTH (WIDTH+10)
Ці директиви замінять в тексті програми кожне слово WIDTH на число 80, а кожне слово LENGTH на вираз (80+10) разом з оточуючими його дужками. Дужки, що містяться в макроозначенні, дозволяють уникнути непорозумінь, пов'язаних з порядком обчислення операцій. Наприклад, за відсутності дужок інструкція:
t = LENGTH*7;
буде приведена до інструкції:
t = 80+10*7;
а не до інструкції:
t = (80+10)*7;
як це виходить за наявності дужок, і в результаті значення змінної t буде 780, а не 630.
У другій синтаксичній формі макросу в директиві #define є список формальних параметрів, який може містити один або декілька ідентифікаторів, розділених комами. Формальні параметри в тексті макроозначення позначають позиції, на які повинні бути підставлені фактичні параметри макровиклику. Кожен формальний параметр може з'явитися в тексті макроозначення скільки завгодно разів.
При макровиклику услід за ідентифікатором макросу записується список фактичних параметрів, кількість яких має співпадати з кількістю формальних параметрів. Наприклад:
#define MAX(x, у) ((x)>(y))?(x):(y)
Ця директива замінить інструкцію:
t = MAX(i, s[i]);
на інструкцію: t = ((i)>(s[i])?(i):(s[i]);
Як і в попередньому прикладі, круглі дужки, в які поміщені формальні параметри в макроозначенні, дозволяють уникнути помилок, пов'язаних з неправильним порядком виконання операторів, якщо фактичні параметри є виразами. Наприклад, за наявності дужок інструкція:
t = MAX(i&j, s[i]||j); буде замінена на інструкцію: t = ((i&j)>(s[i]||j)?(i&j):(s[i]||j); а за відсутності дужок - на інструкцію: t = (i&j>s[i]||j)?i&j:s[i]||j; в якій умовний вираз обчислюється в абсолютно іншому порядку.
Імена макросів не можна перевантажувати. Рекурсивні виклики макросів також створюють проблеми для препроцесора:
#define PRINT(а,b) cout << (a) << (b)
#define PRINT(а,b,c) cout<<(a)<<(b)<<(c) // цей макрос заміщає
// попередній
#define FAC(n) (n > 1)? n * FAC(n-1):1 // проблема – рекурсивний
// виклик макросу
6.3 Директива #undef
Директива #undef використовується для скасування дії директиви #define. Синтаксис цієї директиви наступний:
#undef ідентифікатор
Директива відміняє дію раніше зробленого оголошення| #define для іменованого літерала або макросу з вказаним ідентифікатором. Використання директиви #undef для ідентифікатора, який не був оголошений директивою #define, не є помилкою .
Приклад:
#undef WIDTH
#undef MAX
Ці директиви скасовують оголошення іменованого літерала WIDTH та макросу MAX.
6.4 Директиви умовної компіляції #if, #ifdef, #ifndef, #elif, #else і #endif
Директиви умовної компіляції препроцесора мають наступний синтаксис:
#if expr
#ifdef ідентифікатор
#ifndef ідентифікатор
#elif expr
#else
#endif
Перелічені директиви дають можливість програмісту керувати виконанням директив препроцесора і компіляцією програмного коду. Директива умовної компіляції препроцесора, як правило, обчислює значення цілочисельного константного виразу expr. Операції приведення типів, оператор sizeof і константи типу переліків не можуть бути присутніми у виразах, що обчислюються в директивах препроцесора.
У виразі expr директив умовної компіляції може використовуватися спеціальний оператор defined, який описується нижче.