Препроцесор C
Препроцесор С/С++ — програмний інструмент, який використовується в мовах програмування C та C++, препроцесор, що змінює код програми для подальшої компіляції й збірки.
Мова препроцесора C/C++ не є повною за Тюрінгом хоча б тому, що за допомогою директив неможливо змусити препроцесор «зависнути».
Директивою препроцесора називається рядок у коді, який має такий вигляд #ключове_слово параметри
. Є чітко визначений список ключових слів:
define
— Задає макровизначення (макроси) або константуundef
— Скасовує попереднє визначенняinclude
— Вставляє текст з зазначеного файлу (імпортує)if
— Здійснює умовну компіляцію при істинності константного виразуifdef
— Здійснює умовну компіляцію, якщо константа визначенаifndef
— Здійснює умовну компіляцію при невизначеності константиelse
— Гілка умовної компіляції при хибності висловлюванняelif
— Гілка умовної компіляції, утворена злиттям else та ifendif
— Кінець гілки умовної компіляціїline
— Препроцесор змінює номер поточного рядка й ім'я файлу для компіляціїerror
— Висвітлення повідомлення з зупинки компіляціїpragma
— Дію, що залежить від конкретної реалізації компілятораwarning
— Висвітлення повідомлення без зупинки компіляції- порожнє слово — пуста дія
Константи та макроси препроцесора використовуються для визначення невеликих фрагментів коду. Зазвичай константи визначаються за допомогою великих літер.
// константа
#define BUFFER_SIZE 1024
#define PI 3.14
// макрос
#define NUMBER_OF_ARRAY_ITEMS(array) (sizeof(array)/sizeof(*(array)))
Кожна константа і кожен макрос замінюються відповідним їм визначенням. Макроси мають параметри, схожі на функції, використовуються для зменшення коду.
Приклад визначення макроса max
, який приймає два аргументи: a
і b
та повертає більше з двох число:
#define max( a, b ) ( (a) > (b) ? (a) : (b) )
Макрос викликається як звичайна функція:
z = max(x, y);
Після заміни макроса код виглядатиме так:
z = ((x) > (y) ? (x) : (y));
При виявленні директив #include "..."
або #include <...>
, де «…» — ім'я файлу, препроцесор читає вміст зазначеного файлу, виконує директиви й заміни (підставлення).
Для #include "..."
пошук файлу виконується в цій теці й теках, зазначених в командному рядку компілятора. Для #include <...>
пошук файлу виконується в теках, що містять файли стандартної бібліотеки (шлях до цих тек залежать від компілятора).
Файли, що включаються зазвичай містять:
- оголошення функцій;
- оголошення глобальних змінних;
- визначення інтерфейсів;
- визначення типів даних;
- структури
- та інше.
Директива #include
зазвичай вказується на початку файлу (в заголовку), тому включаються файли називаються заголовками.
Приклад включення файлів зі стандартної бібліотеки мови C:
#include <math.h> // імпортування математичних функцій(стандартна бібліотека)
#include <foo.h> // імпортування файлу не зі стандартної бібліотеки
Використання препроцесора вважається неефективним з наступних причин:
- кожен раз при включенні файлів виконуються директиви й заміни (підставляння); компілятор міг би зберігати результати препроцесорування для використання в майбутньому;
- множинні включення одного файлу доводиться запобігати вручну за допомогою директив умовної компіляції; компілятор міг би виконувати цю задачу самостійно.
Починаючи з 1970-х років стали з'являтися способи, якими замінено включення файлів. У мовах Java і Common Lisp використовуються пакети (ключове слово package
) (див. Package в Java), в мові Паскаль — англ. units (ключові слова unit
і uses
), у мовах Modula, OCaml, D, Haskell і Python — модулі. Використовуються ключові слова module
і import
.
- The C Preprocessor (англ.)