logo search
Подбельский Фомин_Программирование на языке СИ_

3.4. Условная компиляция Директивы ветвлений.

Директивы ветвлений. Условная компиляция обеспечивается в языке Си следующим набором директив, которые, не точно соответствуя названию, управляют не компиляцией, а препроцессорной обработкой текста:

#if целочисленное_константное_выражение

#ifdef идентификатор

#ifndef идентификатор

#else

#endif

#elif

Первые три директивы выполняют проверку условий, две следующие - позволяют определить диапазон действия проверяемого условия. (Директиву #elif рассмотрим несколько позже.) Общая структура применения директив условной компиляции такова:

#if...

текст_1

#else

текст_2

#endif

Конструкция #else текст_2 необязательна. Текст_1 включается в компилируемый текст только при истинности проверяемого условия (обозначено многоточием после #if). Если условие ложно, то при наличии директивы #else на компиляцию передается текст_2. Если директива #else и текст_2 отсутствуют, то весь текст от #if до #endif при ложном условии опускается. Различие между формами команд #if состоит в следующем.

В первой из перечисленных директив

#if целочисленное_константное_выражение

проверяется значение константного выражения, в которое могут входить целые константы и идентификаторы. Идентификаторы могут быть определены на препроцессорном уровне, и тогда их значение определяется подстановками. В противном случае считается, что идентификаторы имеют нулевые значения. Если константное выражение отлично от нуля, то считается, что проверяемое условие истинно. Например, в результате выполнения директив:

#if 5+4

текст_1

#endif

текст 1 всегда будет включен в компилируемую программу. В директиве #ifdef идентификатор

проверяется, определен ли с помощью директивы #define к текущему моменту идентификатор, помещенный после #ifdef. Если идентификатор определен, т.е. является препроцессорным, то текст_1 используется компилятором.

В директиве

#ifndef идентификатор

проверяется обратное условие - истинным считается неопределенность идентификатора, т.е. тот случай, когда идентификатор не был использован в команде #define или его определение было отменено командой #undef.

Условную компиляцию удобно применять при отладке программ для включения или исключения средств вывода контрольных сообщений. Например,

Таких вызовов функции print( ), появляющихся в программе в зависимости от определенности идентификатора DEBUG, может быть несколько, и, убрав либо поместив в скобки комментария /*...*/ директиву #define DEBUG, сразу же отключаем все отладочные средства.

Файлы, предназначенные для препроцессорного включения в программу, обычно снабжают защитой от повторного включения. Такое повторное включение может произойти, если несколько файлов, в каждом из которых, в свою очередь, запланировано препроцессорное включение одного и того же файла, объединяются в общий текст программы. Например, такими средствами защиты снабжены все заголовочные файлы стандартной библиотеки. Схема защиты от повторного включения может быть такой:

Здесь _FILE_NAME - зарезервированный для файла filename препроцессорный идентификатор, который желательно не использовать в других текстах программы.

Для организации мультиветвлений во время обработки препроцессором исходного текста программы введена директива

#elif целочисленное_константное_выражение

Требования к целочисленному_константному_выражению те же, что и в директиве #if.

Структура исходного текста с применением этой директивы такова:

#if условие

текст_для_if

#elif выражение _1

текст_1

#elif выражение _2

текст_2

#else

текст_для_случая_else

#endif

Препроцессор проверяет вначале условие в директиве #if. Если оно ложно (равно 0), - вычисляется выражение_1, если при этом оказывается, что и значением выражения_1 также является 0, - вычисляется выражение_2, и т.д. Если все выражения ложны (равны 0), то в компилируемый текст включается текст_для_случая_else. В противном случае, т.е. при появлении хотя бы одного истинного выражения (в #if или в #elif), начинает обрабатываться текст, расположенный непосредственно за этой директивой, а все остальные директивы не рассматриваются.

Таким образом, препроцессор обрабатывает всегда только один из участков текста, выделенных директивами условной компиляции.