Ще одним способом організації умовної компіляції є використання директив #ifdef (if defined – за умови визначеності) і #ifndef (if not defined – за умови невизначеності). Синтаксис їх однаковий:
#ifdef ім’я_макросу
фрагмент_програми
#endif
#ifndef ім’я_макросу
фрагмент_програми
#endif
Обидві директиви перевіряють, чи був макрос, ім’я якого задається в цій директиві, попередньо визначений директивою #define. При цьому не вимагається, щоб макрос мав повне визначення, достатньо оголошення
#define ім’я_макросу
Фрагмент програми, обмежений директивами #ifdef … #ifndef буде компілюватись за умови, що ім’я макросу попередньо визначене. Дія директиви #ifndef протилежна – фрагмент програми передається на компіляцію, якщо заданий макрос не визначений.
Умовну компіляцію можна використовувати для налагодження програм. Зокрема, в текст програм можна включити ряд контрольних перевірок і повідомлень, що допоможуть відстежити процес виконання програми. Наприклад:
#include <iostream>
using namespace std;
#define DEBUG
int main() {
int a = 123;
#ifdef DEBUG
cout << "a = " << a << endl;
#endif
}
Таких контрольних точок можна створити в програмі декілька. Коли від лагодження програми завершене, достатньо закоментувати директиву #define DEBUG або використати директиву #undef DEBUG.
Директиви #ifdef і #ifndef використовують в заголовних файлах для захисту програми від повторних включень заголовних файлів, які можуть виникнути через вкладення директив #include. Оголосимо структуру для збереження інформації про студентів в окремому файлі:
// student.h
#ifndef STUDENT_H
#define STUDENT_H
struct student {
int id;
int rating;
student * next;
};
#endif
#include <iostream>
#include "student.h"
using namespace std;
int main() {
struct student * p;
…
У разі повторного підключення файлу student.h макрос STUDENT_H вже буде визначеним і директива #ifndef пропустить препроцесування вмісту файлу.
Перевірити, чи ім’я заданого макросу визначене, можна також за допомогою операції defined, яка застосовується в виразах умови директив #if i #elif і має такий синтаксис:
defined ім’я_макросу
Якщо даний макрос був попередньо визначений, то результат операції буде істинний. До результатів цієї операції можна застосовувати логічні операції !, &&, ||. Попередній приклад можна записати так:
#if !defined STUDENT_H
#define STUDENT_H
struct student {
int id;
int rating;
student * next;
};
#endif
За допомогою цієї операції можна перевіряти визначеність декількох макросів:
#if defined A && defined B
…
#endif