Директиви препроцесора

Існує 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 є те, що для зберігання свого значення їм потрібна пам'ять в об'ємі, відповідному їх типу, тоді як для іменованих літералів не вимагається ніякої додаткової пам'яті.

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