Існує 14 стандартних директив препроцесора:
#include
#define
#undef
#if
#ifdef
#ifndef
#elif
#else
#endif
#error
#import
#line
#pragma
#using
Директива #include забезпечує включення в програму текстів інших програмних файлів і неодноразово зустрічалася раніше в прикладах. Призначення та використання цієї директиви детально описано в попередньому розділі. В подальших підрозділах даного розділу дається опис решти директив препроцесора.
Правильно підбираючи та використовуючи директиви, особливо директиву #define, на мові C++ можна писати дуже ефективності програми, які по швидкості виконання і по використовуваній оперативній пам'яті не будуть поступатися добре написаних програм на машино - орієнтованих мовах програмування типу Асемблера.
Потрібно пам'ятати, що препроцесор, який обробляє директиви, працює з рядками символів і практично нічого не знає про синтаксис, змінні, типи даних, оператори, інструкції та області видимості мови C++. Компілятор має справу вже з текстом програми, повністю обробленим препроцесором, і тому помилки макропрограмування, що привели до помилок у програмі на мові C++, можуть діагностуватися вже під час компіляції. Це в значній мірі ускладнює розробку і перевірку програм з макрозасобами. Така ціна ефективності програм, яку, як правило, іншими засобами досягти не можна.
6.2 Директива #define
Директива #define служить для заміни часто використовуваних літералів, ключових слів, послідовностей інструкцій або виразів зручнішими у використанні ідентифікаторами.
Ідентифікатори, що замінюють текстові або числові літерали, називають іменованими літералами.
Ідентифікатори, що замінюють цілі фрагменти програм, називають макросами, причому макроси можуть мати параметри. Сам замінюючий текст в оголошенні макросу називається макроозначенням. Результат підстановки макроозначення в місці кожного виклику макросу, можливо з урахуванням параметрів, називається макророзширенням.
6.2.1 Іменовані літерали
Формат директиви препроцесора #define, яка оголошує іменований літерал, наступний:
#define ідентифікатор літерал
Після появи цього рядка всі імена, що далі зустрілися в тексті програми та які співпали з ідентифікатором, заданим як перший елемент директиви, будуть автоматично замінені на вказаний в директиві літерал перше, ніж почнеться компіляція програми. Наприклад, після появи в програмі директиви:
#define PI 3.14159
всі подальші входження в текст програми іменованого літерала PI будуть замінені на чисельний літерал 3.14159.
Директива дає можливість програмісту присвоїти літералу ім'я і використовувати його далі в програмі замість літерала. Якщо виникне необхідність змінити значення літерала у всій програмі, для цього досить буде внести тільки одну зміну в директиву препроцесора #define і перекомпілювати програму; значення літерала буде змінено в усій програмі автоматично.
Відзначимо, що в директиві все, що знаходиться праворуч від ідентифікатора, є текстом, що заміщає цей ідентифікатор. Наприклад, після виконання директиви:
#define PI =3.14159
препроцесор замінить всі імена PI на текст =3.14159. Багато логічних і синтаксичних помилок виникають внаслідок нерозуміння цього правила.
Повторне оголошення значення іменованого літерала також є типовим джерелом помилок.
Слід відмітити, що в мові C++ віддається перевага використанню іменованих змінних типу const, а не іменованих літералів. Константні змінні є даними визначеного типу, і їх імена видно налагоджувачу. А якщо використовується іменований літерал, то після того, як ідентифікатор був замінений на відповідний текст, тільки цей текст і буде видно налагоджувачу.
Недоліком змінних типу const є те, що для зберігання свого значення їм потрібна пам'ять в об'ємі, відповідному їх типу, тоді як для іменованих літералів не вимагається ніякої додаткової пам'яті.
Використання ясних за сенсом ідентифікаторів для іменованих літералів покращує розуміння тексту програми, робить текст самодокументованим.