11.3. Директиви препроцесора
Директиви препроцесора використовують для керування процесом компіляції та попередньої модифікації тексту програми. Наведемо ті директиви, які визначаються стандартом мови С++.
Наступні директиви називають директивами умовної компіляції:
#if
#ifdef
#else
#elif
#endif
Вони дозволяють або не дозволяють виконувати компіляцію певних частин програми залежно від виконання умов. Такі директиви застосовуються, якщо треба отримати декілька версій виконуваного коду програми.
Директива
#include
дозволяє включити текст з іншого файлу у текст поточної програми. Наприклад, такий рядок
#include "dalmat.h"
призведе до включення файлу "dalmat.h" у текст програми замість рядка самої директиви. Файл "dalmat.h" буде шукатися спочатку у поточній папці. Якщо там його не буде знайдено, пошук буде здійснюватися у системних папках, шляхи до яких прописані у оточенні процесору (у тому числі у каталозі \INCLUDE ). Ім’я файлу можна писати також у кутових дужках:
#include <dalmat.h>
У такому разі пошук файлу для заміни буде вестись одразу у системних папках.
Директива
#define
дозволяє створювати макроси. Розрізняють три види макросів:
- макроси - ідентифікатори;
- макроси заміни;
- макрос-функції.
Макроси-ідентифікатори мають найпростішу конструкцію:
#define ім’я
Застосовуються вони для умовної компіляції.
Макрос заміни записується таким чином:
#define ім’я послідовність_символів
Такий макрос призводить до заміни в тексті програми всіх входжень послідовності символів ім’я на вказану послідовність_символів.
Макрос-функція теж призводить до певних замін, але вони виконуються по іншим правилам. Розглянемо такий приклад:
#define MIN(A,B) ((A)<(B)) ? (A) : (B)
Тепер кожен раз, коли у програмі буде зустрічатися MIN(A,B), замість нього буде підставлено вираз ((A)<(B)) ? (A) : (B). При цьому у якості A,B може бути записано довільну послідовність символів.
Наведемо два приклади більш-менш вдалого використання макросу заміни та макрос-функції. У стандартному пакеті Borland C++ константу розташовано під ім’ям M_PI . Якщо ми бажаємо використовувати її під більш зручним ім’ям pi , треба у початку програми записати такий макрос:
#define pi M_PI
У такий же спосіб можна замість імені математичної функції обчислення тангенсу tan(x) задіяти більш звичне у математиці ім’я tg(x). Для цього потрібно записати такий макрос:
#define tg(x) tan(x)
У старих технологіях програмування макроси заміни часто використовуються для реалізації у програмі констант, а макрос-функції можуть застосовуватись як звичайні функції. Але тут має сенс застережити: в обох вказаних випадках будуть суттєво послаблені можливості діагностування помилок на етапі компіляції. У зв’язку з цим рекомендується застосовувати інші засоби для реалізації констант і функцій.
Наступна директива
#undef ім’я
відміняє визначення макросу з вказаним ім’ям.
Директива
#line
застосовується для зміни змісту напередвизначених макросів __LINE__ та __FILE__ . Макрос __LINE__ містить у собі номер рядка у тексті програми, а макрос __FILE__ - ім’я вихідного файлу з програмою, яка виконується.
Директива
#error повідомлення
припиняє компіляцію з виведенням повідомлення.
Директива
#pragma ім’я
застосовується для надання певної інструкції компілятору.
Ряд макросів, які є символьними константами, компілятор визначає автоматично. Такі макроси діляться на дві групи: макроси ANSI і додаткові макроси, які генеруються компілятором (наприклад, компілятором Borland C++). До макросів ANSI, зокрема, відносяться такі:
__DATE__ - рядок, що містить дату компіляції у форматі mmm dd yyyy;
__TIME__ - рядок, що містить час компіляції у форматі hh:mm:ss;
__FILE__ - рядок, що містить ім'я поточного файлу в подвійних лапках.
Умовна компіляція фрагменту програми
Розглянемо деякий фрагмент програми (звичайно це є набір функцій). Запишемо його із застосуванням директив #ifdef та #endif у такий спосіб:
#ifdef _MM_
... фрагмент програми ...
#endif
Тепер, якщо у початку програми макрос-ідентифікатор _MM_ визначено (за допомогою директиви #define), то фрагмент програми комплюватися буде, якщо ж ні, то позначений фрагмент програми при компіляції буде проігноровано.
Запобігання повторного включення h-файлу
У зв'язку з тим, що обробка директив включення #include часто призводить до неявних рекурсій, препроцесорна обробка програми може приводити до того, що в модифікований текст програми деякий h-файл буде включено більш, ніж один раз. Останнє призводить до дублювання визначень і, як результат, до помилок компіляції. Для того, щоб уникнути повторного включення h-файлів можна використати директиви умовної компіляції #ifndef і #endif у сполученні з директивою визначення простого макросу #define. Такий h-файл може бути влаштований у такий спосіб:
#ifndef MYFILE_H
#define MYFILE_H
… текст h-файлу …
#endif
Контроль параметрів компіляції
Borland C++ визначає ряд макросів, що дозволяють контролювати параметри компіляції, наприклад:
__LARGE__ - визначений, якщо встановлено модель пам'яті LARGE;
__FLAT__ - визначений, якщо встановлено режим побудови 32-бітної прикладної програми з моделлю пам'яті FLAT.
Більш детальну інформацію щодо застосування директив можна подивитись у [7].
- Запоріжжя знту 2008
- Глава 1
- 1.1. Історія та сучасність
- 1.2. Загальна структура програми. Два простих приклади
- Void main()
- Void main()
- Глава 2 Об’єкти та ідентифікатори
- 2.1. Об'єкти та їхні атрибути
- 2.2. Алфавіт мови та лексеми
- 2.3. Ідентифікатори
- Void main() // (рівень 0)
- 2.4. Вправи
- Глава 3
- 3.1. Поняття виразу. Вирази Lvalue та Rvalue
- 3.2. Операції. Пріоритети та асоціативність
- Void main()
- Void main()
- Void main()
- 3.3. Вправи
- Глава 4
- 4.1. Види операторів
- 4.2. Стандартні оператори
- If (лв) опер_1; [ else опер_2; ]
- Void main()
- If (лв1) опер_1;
- Void main()
- Void main()
- Void main()
- Void main()
- Void main()
- 4.3. Оголошення змінних та ініціалізація
- Int number(125);
- Int number(125);
- 4.4. Константи і константні об'єкти
- Void main()
- 4.5. Вправи
- Глава 5
- 5.1. Типи та їхні різновиди
- 5.2. Службове слово void
- Int a[small], a[large];
- 5.4. Перетворення типів
- 5.5. Вправи
- Void main()
- Глава 6 покажчики і посилання
- 6.1. Покажчики
- Void main()
- Void strcpy(char* s1, char* s2)
- Void* pv;
- 6.2. Посилання
- 6.3 Вправи
- Void main()
- Глава 7 масиви і динамічні об'єкти
- 7.1. Масиви
- Void main()
- 7.2. Рядки символів
- Int strlen(char* s);
- Int strcmp(char* s1, char* s2);
- Int len(char *s)
- 7.3. Динамічні об'єкти й масиви
- Void main()
- Void main()
- Void main()
- Void main()
- 7.4. Вправи
- Глава 8 функції та процедури
- 8.1. Загальні відомості
- Void main()
- Void c_mul(double a_re, double a_im,
- Void swap1(long *px, long *py)
- Void swap2(long &X, long &y)
- Void main()
- Int fun(int, float*, double&);
- Void fun(int n)
- Void main()
- Void swap(int& a, int& b)
- 8.2. Функція main
- Void або int main(int n, char** p, char** q);
- Void main(int n, char** p, char** q)
- 8.3. Функції зі змінною кількістю параметрів
- Void main()
- 8.4. Покажчики на функції
- Int (*pf[3])(float X, float y);
- Void main()
- 8.5. Функції з шаблонами
- Void swap(string& s1, string& s2)
- Void main()
- Inline t abs(t X)
- Void create(type* &a, int n)
- Void del(type* &a)
- 8.6. Вправи
- Глава 9 консольне вВедення / вИведення
- 9.1. Засоби бібліотеки с
- Void main()
- Void main()
- Void main()
- 9.2. Використання потоків
- Void main()
- Void main()
- 9.3. Вправи
- Глава 10 операції з файлами
- 10.1. Види файлів і режими роботи з ними
- 10.2. Бібліотека с
- Void fprint(file* f, &X)
- Void rewind(file* f);
- Void main()
- Void main(int n, char** f)
- Void main(int n, char** fnam)
- 10.3. Застосування потоків
- Ifstream fin;
- Void open(char* filename, int mode, int access);
- Ifstream fin("a.Dat");
- Void main()
- Ifstream fa("a.Dat");
- If (fa.Eof()) break;
- Void main(int n, char** fnam)
- Void main(int n, char** f)
- Ifstream fa(f[1]);
- Void main(int n, char** fnam)
- Void main()
- Ifstream in("example.Cpp");
- Void main()
- Ifstream in("name.Dat",ios::binary);
- Void main()
- Ifstream fa("a.Dat");
- Int descr(fstr& f)
- Void main()
- Ifstream ina(infa);
- Ifstream inb(infb);
- 10.4. Вправи
- Глава 11 компіляція програми. Директиви і макроси
- 11.1. Фази компіляції
- 11.2. Директиви режиму компіляції
- 11.3. Директиви препроцесора
- 11.4. Вправи
- Глава 12 змішане програмування. Використання ассемблерного коду
- 12.1. Засоби використання асемблера
- Void main()
- 12.2. Вправи
- Глава 13 програмна реалізація алгоритмів
- 13.1. Алгоритм Евкліда пошуку найбільшого загального дільника двох цілих чисел
- 13.2. Обчислення факторіала
- 13.3. Пошук простих чисел. Решето Ератосфена
- Void main()
- 13.4. Генерація підмножин
- Void main()
- 13.5. Сортування масивів
- Void main()
- Void main()
- Int flag;
- 13.6. Пошук у масиві
- Void main()
- Void main()
- 13.7. Операції з матрицями
- Void ObrMatr (double** a, double** b, unsigned n )
- Void Minv(double** a, double** b, unsigned n )
- Void Gauss(double** a, double* b, double* X, unsigned n)
- 13.8. Лінійна інтерполяція даних
- 13.9. Лінійна апроксимація
- Void linappr(int n, double* X, double* y, double& a0, double& a1)
- 13.10. Розв’язування нелінійних рівнянь
- Void Bisec(funx f, double a, double b, double eps, double& X)
- Void Bisec(funx f, double a, double b,
- Void main()
- 13.11. Пошук заданої послідовності символів у файлі
- Void push(char* s, int n, char X)
- Void main()
- 13.12. Вправи
- Глава 14 створення й використання бібліотечних модулів. Модуль syst.H
- 14.1. Створення бібліотечних модулів
- 14.2. Модуль syst.H
- 14.3. Системні функції та макрооперації
- Void runtimer();
- Void main()
- Void runstimer();
- Void main()
- Void swp(Type& a, Type& b);
- Void main()
- Int cmp(int nx, int ny, Type* X, Type* y);
- Void errhalt(bool ex, char* mes);
- Void errhalt(bool ex, char* mes, file* f);
- 14.4. Операції введення/виведення
- Void flushkey();
- Void main()
- Int getyes();
- Void main()
- Void main()
- Int weight(type X);
- Void main()
- Int hamdist(Type a, Type b)
- 14.6. Спеціальні класи. Клас Spline для інтерполяції даних сплайнами
- Void main()
- 14.7. Вправи
- Глава 15 графІчне виведення. Модуль rgraph.H
- 15.1. Модуль rgraph.H. Загальні відомості
- 15.2. Глобальні змінні й константи
- 15.3. Класи й покажчики на функції
- 15.4. Функції й процедури класів
- Void main()
- Void main()
- Void main()
- Void main()
- 15.7. Вправи
- Глава 16 Життєвий цикл програмного продукту. Питання стилю запису програм
- 16.1. Життєвий цикл програми
- Int n, alfa, col;
- Void draw();
- Void rotate(int delta);
- 16.3. Вправи
- Література