Макроси

Директива #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, який описується нижче.