Инструкция которая является средством условной компиляции

Условная компиляция

Последнее обновление: 07.01.2023

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

Прежде всего это такие директивы как #if/#else/#endif, действие которых напоминает условную конструкцию if:

#if условие
исходный_код
#endif

Если условие возвращает ненулевое значение (то есть оно истинно), то в итоговый исходный файл вставляется исходный код, который расположен между директивами
#if и #endif:

#include <stdio.h>
#define N 22

int main(void)
{
#if N==22
	printf("N=22");
#endif
	return 0;
}

Директива #else позволяет задать альтернативый код, который компилируется, если условие не верно:

#include <stdio.h>
#define N 22
 
int main(void)
{
#if N==22
    printf("N=22");
#else
    printf("N is undefined");
#endif
    return 0;
}

С помощью директивы #elif можно проверять дополнительные условия:

#include <stdio.h>
#define N 24

int main(void)
{
#if N==22
	printf("N = 22");
#elif N==24
	printf("N=24");
#else
	printf("N is undefined");
#endif
	return 0;
}

#ifdef

С помощью директивы #ifdef можно проверять, определен ли идентификатор, и если он определен, вставлять в исходный код определенный текст:

#include <stdio.h>
#define DEBUG

int main(void)
{
#ifdef DEBUG
	printf("Debug mode");
#endif
	return 0;
}

Обратным действием обладает директива #ifndef — она включает текст, если идентификатор не определен:

#include <stdio.h>
//#define DEBUG

int main(void)
{
#ifndef DEBUG
	printf("Production mode");
#else
	printf("Debug mode");
#endif
	return 0;
}

Если нам одновременно надо проверить значения двух идентификаторов, то можно использовать специальный оператор defined:

#include <stdio.h>
#define BETA
#define DEBUG

int main(void)
{
#if defined DEBUG && !defined BETA
	printf("debug mode; final version");
#elif defined DEBUG && defined BETA
	printf("debug mode; beta version");
#else
	printf("undefined mode");
#endif
	return 0;
}

Содержание

Имеется несколько директив, которые дают возможность выборочно компилировать части исходного кода вашей программы. Этот процесс называется условной компиляцией и широко используется фирмами, живущими за счет коммерческого программного обеспечения — теми, которые поставляют и поддерживают многие специальные версии одной программы.

Директивы #if, #else, #elif и #endif

Возможно, самыми распространенными директивами условной компиляции являются #if, #else, #elif и #endif. Они дают возможность в зависимости от значения константного выражения включать или исключать те или иные части кода.

В общем виде директива #if выглядит таким образом:

#if константное выражение
    последовательность операторов
#endif

Если находящееся за #if константное выражение истинно, то компилируется код, который находится между этим выражением и #endif. В противном случае этот промежуточный код пропускается. Директива #endif обозначает конец блока #if. Например,

/* Простой пример #if. */
#include <stdio.h>

#define MAX 100

int main(void)
{
#if MAX>99
  printf("Компилирует для массива, размер которого больше 99.\n");
#endif

  return 0;
}

Это программа выводит сообщение на экран, потому что МАХ больше 99. В этом примере показано нечто очень важное. Значение выражения, находящегося за директивой #if, должно быть вычислено во время компиляции. Поэтому в этом выражении могут находиться только ранее определенные идентификаторы и константы, — но не переменные.

Директива #else работает в основном так, как else — ключевое слово языка С: задает альтернативу на тот случай, если не выполнено условие #if. Предыдущий пример можно дополнить следующим образом:

/* Простой пример #if/#else. */
#include <stdio.h>

#define MAX 10

int main(void)
{
#if MAX>99
  printf("Компилирует для массива, размер которого больше 99.\n");
#else
  printf("Компилирует для небольшого массива.\n");
#endif 

  return 0;
}

В этом случае выясняется, что МАХ меньше 99, поэтому часть кода, относящаяся к #if, не компилируется. Однако компилируется альтернативный код, относящийся к #else, и откомпилированная программа будет отображать сообщение Компилируется для небольшого массива.

Обратите внимание, что директива #else используется для того, чтобы обозначить и конец блока #if, и начало блока #else. Это естественно, поскольку любой директиве #if может соответствовать только одна директива #endif.

Директива #elif означает «else if» и устанавливает для множества вариантов компиляции цепочку if-else-if. После #elif находится константное выражение. Если это выражение истинно, то компилируется находящийся за ним блок кода, и больше не проверяются никакие другие выражения #elif. В противном же случае проверяется следующий блок этой последовательности. В общем виде #elif выглядит таким образом:

#if выражение
    последовательность операторов
#elif выражение 1
    последовательность операторов
#elif выражение 2
    последовательность операторов
#elif выражение 3
    последовательность операторов
#elif выражение 4
.
.
.
#elif выражение N
    последовательность операторов
#endif

Например, в следующем фрагменте для определения знака денежной единицы используется значение ACTIVE_COUNTRY (для какой страны):

#define US 0 
#define ENGLAND 1
#define FRANCE 2

#define ACTIVE_COUNTRY US

#if ACTIVE_COUNTRY == US
  char currency[] = "dollar";
#elif ACTIVE_COUNTRY == ENGLAND
  char currency[] = "pound";
#else
  char currency[] = "franc";
#endif

В соответствии со стандартом С89 у директив #if и #elif может быть не менее 8 уровней вложенности. А в соответствии со стандартом С99 программистам разрешается использовать не менее 63 уровней вложенности. При вложенности каждая директива #endif, #else или #elif относится к ближайшей директиве #if или #elif. Например, совершенно правильным является следующий фрагмент кода:

#if MAX>100
  #if SERIAL_VERSION
    int port=198;
  #elif
    int port=200;
  #endif
#else
  char out_buffer[100];
#endif

Директивы #ifdef и #ifndef

Другой способ условной компиляции — это использование директив #ifdef и #ifndef, которые соответственно означают «if defined» (если определено) и «if not defined» (если не определено). В общем виде #ifdef выглядит таким образом:

#ifdef имя_макроса
       последовательность операторов
#endif

Блок кода будет компилироваться, если имя макроса было определено ранее в операторе #define.

В общем виде оператор #ifndef выглядит таким образом:

#ifndef имя_макроса
        последовательность операторов
#endif

Блок кода будет компилироваться, если имя макроса еще не определено в операторе #define.

И в #ifdef, и в #ifndef можно использовать оператор #else или #elif. Например,

#include <stdio.h>

#define TED 10

int main(void)
{
#ifdef TED
  printf("Привет, Тед\n");
#else
  printf("Привет, кто-нибудь\n");
#endif
#ifndef RALPH
  printf("А RALPH не определен, т.ч. Ральфу не повезло.\n");
#endif

  return 0;
}

выведет Привет, Тед, а также A RALPH не определен, т.ч. Ральфу не повезло.

В соответствии со стандартом С89 допускается не менее 8 уровней #ifdef и #ifndef. А стандарт С99 устанавливает, что должно поддерживаться не менее 63 уровней вложенности.

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

Основные директивы препроцессора Си

Подключение файлов
#include — Вставляет текст из указанного файла.

Макроопределения
#define — Задаёт макроопределение (макрос) или символическую константу.
#undef — Отменяет предыдущее определение символической константы.

Условная компиляция
#if — Осуществляет условную компиляцию, если константное выражение истинно.
#ifdef — Осуществляет условную компиляцию, если символическая константа была определена.
#ifndef — Осуществляет условную компиляцию, если символическая константа не определена.
#elif — Выполняется, если предыдущие условия конструкции if или elif ложны.
#else — Выполняется, если все условия конструкции if или elif ложны.
#endif — Конец ветки условной компиляции.

#if defined — Тоже самое, что и #ifdef.*
#if !defined — Тоже самое, что и #ifndef.*
*Обе эти конструкции можно использовать для визуального облегчения восприятия сложных условий, когда веток конструкций несколько. Например так можно обозначить главный блок, внутри которого несколько иных условий.

У каждой директивы #if в исходном файле должна быть соответствующая закрывающая директива #endif.

#if Константное выражение
  Если истинно..
#elif константное выражение
  Или..
#elif константное выражение
  Или..
#else
  Или..
#endif

Между директивами #if и #endif может располагаться любое количество директив #elif, однако допускается не более одной директивы #else. Если присутствует директива #else, она должна быть последней перед директивой #endif.

Диагностика
#line — Препроцессор изменяет номер текущей строки и имя компилируемого файла.
#error — Выдача диагностического сообщения и прерывает компиляцию.
#warning — Выдает диагностическое сообщение, но не прерывает компиляцию.

Дополнительные опции для компилятора
#pragma — Дополнительные опции, зависящие от конкретного компилятора. В каждом компиляторе существуют свои опции.

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

В примере приведены условия для компиляторов С/С+ — IAR Embedded Workbench, CodeVisionAVR и AVR GCC.

/* ------------------------------------------------------------------------ */
#if defined __IAR_SYSTEMS_ICC__  /* if IAR */
/* ------------------------------------------------------------------------ */

	/* Тут ваш код для компилятора IAR */

/* ------------------------------------------------------------------------ */
#elif __CODEVISIONAVR__ /* if CodeVisionAVR */
/* ------------------------------------------------------------------------ */

	/* Тут ваш код компилятора CodeVisionAVR */

/* ------------------------------------------------------------------------ */
#else   /* if default, then development for avr-gcc/avr-libc */
/* ------------------------------------------------------------------------ */

	/* Тут ваш код для компилятора avr-gcc/avr-libc */

#endif /* default development */

Инструкции

ОО-нотация, разработанная в этой книге, императивна: вычисления специфицируются через команды (commands), также называемые инструкциями (instructions). (Мы избегаем обычно применимого термина оператор (предложение) (statement), поскольку в слове есть оттенок выражения, описывающего факты, а хотелось подчеркнуть императивный характер команды.)

Для имеющих опыт работы с современными языками инструкции выглядят как хорошие знакомые. Исключение составляют некоторые специальные свойства циклов, облегчающие их верификацию. Вот список инструкций: Вызов процедуры, Присваивание, Условие, Множественный выбор, Цикл, Проверка, Отладка, Повторное выполнение, Попытка присваивания.

Вызов процедуры

При вызове указывается имя подпрограммы, возможно, с фактическими аргументами. В инструкции вызова подпрограмма должна быть процедурой. Вызов функции является выражением. Хотя сейчас нас интересуют инструкции, следующие правила применимы в обоих случаях.

Вызов может быть квалифицированным или неквалифицированным. Для неквалифицированного вызова подпрограммы из включающего класса в качестве цели используется текущий экземпляр класса. Этот вызов имеет вид:

r (без аргументов), или

r (x, y, …) (с аргументами)

Квалифицированный вызов явно называет свою цель, заданную некоторым выражением. Если a — выражение некоторого типа, C — базовый класс этого типа, а — q одна из программ C, то квалифицированный вызов имеет форму a.q. Опять же, за q может следовать список фактических аргументов; a может быть неквалифицированным вызовом функции с аргументами, как в p (m).q (n), где p(m) — это цель. В качестве цели можно также использовать более сложное выражение при условии заключения его в скобки, как в (vector1 + vector2).count.

Также разрешаются квалифицированные вызовы с многоточием в форме: a.q1q2 …qn, где a, так же, как и qi, может включать список фактических аргументов.

Экспорт управляет применением квалифицированных вызовов. Напомним, что компонент f, объявленный в классе B, доступен в классе A ( экспортирован классу ), если предложение feature, объявляющее f, начинается с feature (без дальнейшего уточнения) или feature {X, Y,… }, где один из элементов списка {X, Y,…} является A или предком A. Имеет место:

Правило Квалифицированного Вызова

Квалифицированный вызов вида b.q1. q2…. qn, появляющийся в классе C корректен, только если он удовлетворяет следующим условиям:

  1. Компонент, стоящий после первой точки, q1, должен быть доступен в классе C.
  2. В вызове с многоточием, каждый компонент после второй точки, то есть каждое qi для i > 1, должен быть доступен в классе C.

Чтобы понять причину существования второго правила, отметим, что a.q.r.s — краткая запись для

которая верна только, если q, r и s доступны классу C, в котором появляется этот фрагмент. Не имеет значения, доступно ли r базовому классу типа q, и доступно ли s базовому классу типа r.

Вызовы могут иметь инфиксную или префиксную форму. Выражение a + b, записанное в инфиксной форме, может быть переписано в префиксной форме: a.plus (b). Для обеих форм действуют одинаковые правила применимости.

Присваивание (Assignment)

Инструкция присваивания записывается в виде:

где x — сущность, допускающая запись (writable), а e — выражение совместимого типа. Такая сущность может быть:

  • неконстантным атрибутом включающего класса;
  • локальной сущностью включающей подпрограммы. Для функции допустима сущность Result.

Сущности, не допускающие запись, включают константные атрибуты и формальные аргументы программы — которым, как мы видели, подпрограмма не может присваивать новое значение.

Создание (Creation)

Инструкция создания изучалась в предыдущих лекциях3См. «Инструкция создания» и «Процедуры создания»,
«Динамические структуры: объекты»
. Один из вариантов рассмотрен в «Полиморфное создание»,
«Введение в наследование»
.
в двух ее формах: без процедуры создания, как в create x, и с процедурой создания, как в create x.p (…). В обоих случаях x должна быть сущностью, допускающей запись.

Условная Инструкция (Conditional)

Эта инструкция задает различные формы обработки в зависимости от выполнения определенных условий. Основная форма:

if boolean_expression then
   instruction; instruction; ...
else
   instruction; instruction; ...
end

где каждая ветвь может иметь произвольное число инструкций (а возможно и не иметь их).

Будут выполняться инструкции первой ветви, если boolean_expression верно, а иначе — второй ветви. Можно опустить часть else, если второй список инструкций пуст, что дает:

if boolean_expression then
   instruction; instruction; ...
end

Когда есть более двух возможных случаев, можно избежать вложения (nesting) условных команд в частях else, используя одну или более ветвей elseif, как в:

if c1 then
   instruction; instruction; ...
elseif c2 then
   instruction; instruction; ...
elseif c3 then
   instruction; instruction; ...
...
else
   instruction; instruction; ...
end

где часть else остается факультативной. Это дает возможность избежать вложения

if c1 then
   instruction; instruction; ...
else
   if c2 then
      instruction; instruction; ...
   else
      if c3 then
         instruction; instruction; ...
...
      else
         instruction; instruction; ...
      end
   end
end

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

ОО-метод, благодаря полиморфизму и динамическому связыванию, уменьшает необходимость явных условных инструкций и множественного выбора, поддерживая неявную форму выбора. Когда объект применяет некоторый компонент, имеющий несколько вариантов, то во время выполнения нужный вариант выбирается автоматически в соответствии с типом объекта. Этот неявный стиль выбора обычно предпочтительнее, но, конечно, инструкции явного выбора остаются необходимыми.

Множественный выбор

Инструкция множественного выбора (также известная, как инструкция Case ) производит разбор вариантов, имеющих форму: e = vi, где e — выражение, а vi — константы того же типа. Хотя условная инструкция (if e = v1 then …elseif e = v2 then…) работает, есть две причины, оправдывающие применение специальной инструкции, что является исключением из обычного правила: «если нотация дает хороший способ сделать что-то, нет необходимости вводить другой способ». Вот эти причины:

  • Разбор случаев настолько распространен, что заслуживает особого синтаксиса, увеличивающего ясность, позволяя избежать бесполезного повторения » e = «.
  • Компиляторы могут использовать особенно эффективную технику реализации, — таблицу переходов ( jump table ), — неприменимую к общим условным инструкциям и избегающую явных проверок.

Что касается типа анализируемых величин (тип e и vi ), то инструкции множественного выбора достаточно поддерживать только целые и булевы значения. Согласно правилу, они фактически должны объявляться либо все как INTEGER, либо как CHARACTER. Общая форма инструкции такова:

inspect
   e
when v1 then
   instruction; instruction; ...
when v2 then
   instruction; instruction; ...
...
else
   instruction; instruction; ...
end

Все значения vi должны быть различными; часть else факультативна; каждая из ветвей может иметь произвольное число инструкций или не иметь их.

Инструкция действует так: если значение e равно значению vi (это может быть только для одного из них), выполняются инструкции соответствующей ветви; иначе, выполняются инструкции в ветви else, если они есть.

Если отсутствует else, и значение e не соответствует ни одному vi, то возникает исключительная ситуация («Некорректно проверяемое значение»). Это решение может вызвать удивление, поскольку соответствующая условная инструкция в этом случае ничего не делает. Но оно характеризует специфику инструкции множественного выбора. Когда вы пишете inspect с набором значений vi, нужно включить ветвь else, даже пустую, если вы понимаете, что во время выполнения значения e могут не соответствовать никаким vi. Если вы не включаете else, то это эквивалентно явному утверждению: «значение e всегда является одним из vi «. Проверяя это утверждение и создавая исключительную ситуацию при его нарушении, реализация оказывает нам услугу. Бездействие в данной ситуации — означает ошибку — в любом случае, ее необходимо устранить как можно раньше.

Одно из частых приложений инструкции множественного выбора — анализ символа, введенного пользователем4Это элементарная схема. О более сложных технических приемах обработки пользовательских команд см. лекцию 3 курса «Основы объектно-ориентированного проектирования«.
:

inspect
   first_input_letter
when 'D' then
   "Удалить строку"
when 'I' then
   "Вставить строку"
...
else
   message ("Неопознанная команда; введите H для получения справки")
end

Когда значения vi целые, то они могут быть определены как уникальные (unique values), концепция которых рассмотрена в следующей лекции. Это делает возможным в объявлении определить несколько абстрактных констант, например, Do, Re, Mi, Fa, Sol, La, Si: INTEGER is unique, и затем анализировать их в инструкции: inspect note when Do then…when Re then…end.

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

Циклы

Синтаксис циклов описан при обсуждении Проектирования по Контракту (
«Проектирование по контракту: построение надежного ПО»
):

from
   initialization_instructions
invariant
   invariant
variant
   variant
until
   exit_condition
loop
   loop_instructions
end

Предложения invariant и variant факультативны. Предложение from требуется, хотя и может быть пустым. Оно задает инициализацию параметров цикла. Не рассматривая сейчас факультативные предложения, выполнение цикла можно описать следующим образом. Вначале происходит инициализация, и выполняются initialization_instructions. Затем следует «циклический процесс», определяемый так: если exit_condition верно, то циклический процесс — пустая инструкция ( null instruction ); если условие неверно, то циклический процесс — это выполнение loop_instructions, затем следует (рекурсивно) повторение циклического процесса.

Проверка

Инструкция проверки рассматривалась при обсуждении утверждений (
«Проектирование по контракту: построение надежного ПО»
). Она говорит, что определенные утверждения должны удовлетворяться в определенных точках:

check
   assertion -- Одно или больше предложений
end

Отладка

Инструкция отладки является средством условной компиляции. Она записывается так:

debug instruction; instruction; ... end

В файле управления (Ace-файле) для каждого класса можно включить или отключить параметр debug. При его включении все инструкции отладки данного класса выполняются, при отключении — они не влияют на выполнение.

Эту инструкцию можно использовать для включения специальных действий, выполняющихся только в режиме отладки, например, печати некоторых величин.

Повторение вычислений

Инструкция повторного выполнения рассматривалась при обсуждении исключительных ситуаций (
«Когда контракт нарушается: обработка исключений»
). Она появляется только в предложении rescue, повторно запуская тело подпрограммы, работа которой была прервана.

From Wikipedia, the free encyclopedia

In computer programming, conditional compilation is a compilation technique which results in an executable program that is able to be altered by changing specified parameters. This technique is commonly used when these alterations to the program are needed to run it on different platforms, or with different versions of required libraries or hardware.

Many programming languages support conditional compilation. Typically compiler directives define or «undefine» certain variables; other directives test these variables and modify compilation accordingly. For example, not using an actual language, the compiler may be set to define «Macintosh» and undefine «PC», and the code may contain:

(* System generic code *)
if mac != Null then
    (* macOS specific code *)
else if pc != Null
    (* Windows specific code *)

In C and some languages with a similar syntax, this is done using an ‘#ifdef’ directive.

A similar procedure, using the name «conditional comment», is used by Microsoft Internet Explorer from version 5 to 9 to interpret HTML code. There is also a similar proprietary mechanism for adding conditional comments within JScript, known as conditional compilation.[1]

Criticism[edit]

When conditional compilation depends on too many variables, it can make the code harder to reason about as the number of possible combinations of configuration increases exponentially.[2][3][4] When conditional compilation is done via a preprocessor that does not guarantee syntactically correct output in the source language, such as the C preprocessor, this may lead to hard-to-debug compilation errors,[5][6][7] which is sometimes called «#ifdef hell.»[8][9]

References[edit]

  1. ^ «Conditional Compilation». Microsoft Corporation. Archived from the original on 2008-09-06. Retrieved 2011-11-27.
  2. ^ Gazzillo, Paul; Wei, Shiyi (2019-05-27). «Conditional Compilation is Dead, Long Live Conditional Compilation!» (PDF). ICSE-NIER ’19: Proceedings of the 41st International Conference on Software Engineering: New Ideas and Emerging Results. 2019 IEEE/ACM 41st International Conference on Software Engineering: New Ideas and Emerging Results (ICSE-NIER). Montreal, QC, Canada: IEEE Press. pp. 105–108. doi:10.1109/ICSE-NIER.2019.00035. ISBN 978-1-7281-1758-4. Archived (PDF) from the original on 2022-11-07. Retrieved 2023-01-21.
  3. ^ Meinicke, Jens; Thüm, Thomas; Schröter, Reimar; Benduhn, Fabian; Leich, Thomas; Saake, Gunter (2017). Meinicke, Jens; Thüm, Thomas; Schröter, Reimar; Benduhn, Fabian (eds.). Quality Assurance for Conditional Compilation. Cham: Springer International Publishing. pp. 131–139. doi:10.1007/978-3-319-61443-4_12. ISBN 978-3-319-61443-4. Retrieved 2023-01-21.
  4. ^ «compiler — How does conditional compilation impact product quality, security and code complexity?». Software Engineering Stack Exchange. Retrieved 2023-01-21.
  5. ^ Le, Duc; Walkingshaw, Eric; Erwig, Martin (2011-09-18). #ifdef confirmed harmful: Promoting understandable software variation. 2011 IEEE Symposium on Visual Languages and Human-Centric Computing (VL/HCC). pp. 143–150. doi:10.1109/VLHCC.2011.6070391. ISBN 978-1-4577-1246-3.
  6. ^ «conditional compilation — Why should #ifdef be avoided in .c files?». Stack Overflow. Retrieved 2023-01-21.
  7. ^ «c++ — Dos and Don’ts of Conditional Compile». Stack Overflow. Retrieved 2023-01-21.
  8. ^ Preschern, Christopher (2019-07-03). Patterns to escape the #ifdef hell (PDF). Proceedings of the 24th European Conference on Pattern Languages of Programs. EuroPLop ’19. New York, NY, USA: Association for Computing Machinery. pp. 1–12. doi:10.1145/3361149.3361151. ISBN 978-1-4503-6206-1. Archived (PDF) from the original on 2022-12-21.
  9. ^ «Living in the #ifdef Hell». www.cqse.eu. 28 October 2015. Archived from the original on 2022-11-28. Retrieved 2023-01-21.

Содержание

  • 1. Директивы #if#else#elif#endif. Условная компиляция
    • 1.1. Комбинация директив #if — #endif
    • 1.2. Комбинация директив #if — #else — #endif
    • 1.3. Комбинация директив #if — #elif — #endif. Цепочка if-else-if
  • 2. Директивы #ifdef, #ifndef. Условная компиляция
  • 3. Оператор defined. Определение, существует ли в программе макроимя
  • Связанные темы

1. Директивы #if, #else, #elif, #endif. Условная компиляция
1.1. Комбинация директив #if — #endif

С помощью директив #if, #else, #elif, #endif можно задать части программы, которые нужно откомпилировать в зависимости от значения константного выражения.

Общая форма директивы #if следующая

#if constant_expression
  operators_sequence
#endif

здесь

  • constant_expression – константное выражение, возвращающее true или false;
  • operator_sequence – последовательность операторов, выполняемая если constant_expression=true.

Пример.

#include <iostream>
using namespace std;

// Номер решения
#define N_SOLUTION 2

// Число Пі
#define Pi 3.1415926535

// Если решение равно 1, то вычислить длину окружности
#if N_SOLUTION == 1
#define RESULT(radius) 2*Pi*radius
#endif

// Если решение равно 2, то вычислить площадь круга
#if N_SOLUTION == 2
#define RESULT(radius) Pi*radius*radius
#endif

void main()
{
  // 1. Радиус
  double r;

  // 2. Ввести радиус окружности
  cout << "r = ";
  cin >> r;

  // 3. Вычислить площадь круга
  double area = RESULT(r);
  cout << "area = " << area << endl;
}

Результат выполнения программы

r = 3
area = 28.2743
 
1.2. Комбинация директив #if — #else — #endif

Директивы #if – #else – #endif позволяют откомпилировать одну из двух частей программы в зависимости от значения условного выражения. Общая форма использования данной директивы следующая:

#if consant_expression
  operators_sequence_1
#else
  operators_sequence_2
#endif

здесь

  • constant_expression – выражение, возвращающее значение логического типа (true или false);
  • operators_sequence_1 – последовательность операторов, которую нужно выполнить, если constant_expression=true;
  • operators_sequence_2 – последовательность операторов, которую нужно выполнить, если constant_expression=false.

Пример.

В примере в зависимости от значения N_SOLUTION компилируется нужный фрагмент кода, определяющий длину окружности или площадь круга.

#include <iostream>
using namespace std;

// Номер решения
#define N_SOLUTION 1

// Число Пі
#define Pi 3.1415926535

// Если решение равно 1, то компилируется фрагмент, который вычисляет длину окружности,
// иначе компилируется фрагмент вычисляющий площадь окружности.
#if N_SOLUTION == 1
#define RESULT(radius) 2*Pi*radius
#else
#define RESULT(radius) Pi*radius*radius
#endif

void main()
{
  // 1. Радиус
  double r;

  // 2. Ввести радиус окружности
  cout << "r = ";
  cin >> r;

  // 3. Вычисление
  double result = RESULT(r);
  cout << "result = " << result << endl;
}

Результат

r = 3
result = 18.8496
 
1.3. Комбинация директив #if — #elif — #endif. Цепочка if-else-if

Комбинация директив #if-#elif-#endif образует так называемую цепочку if-else-if или ступенчатую (каскадную) форму директивы #if. Директива #elif расшифровывается как else-if. С помощью данной формы директив можно делать проверку многих условий. Общий вид директивы #if-#elif-#endif следующий

#if expression_1
  operators_sequence_1
#elif expression_2
  operators_sequence_2
...
#elif expression_N
  operators_sequence_N
#endif

здесь

  • expression_1, expression_2, expression_N – некоторое выражение, которое может бать истинно (true) или не истинно (false);
  • operators_sequence_1, operators_sequence_2, operators_sequence_N – последовательность операторов, которые нужно откомпилировать в соответствующем блоке кода.

Пример.

В примере, в зависимости от значения N_SOLUTION вычисляется соответствующая формула

#include <iostream>
using namespace std;

// Номер решения
#define N_SOLUTION 3

// Число Пі
#define Pi 3.1415926535

// Компилируется фрагмент, который соответствует номеру номеру решения.
#if N_SOLUTION == 1
#define RESULT(radius) 2*Pi*radius // длина окружности
#elif N_SOLUTION == 2
#define RESULT(radius) Pi*radius*radius // площадь круга
#elif N_SOLUTION == 3
#define RESULT(radius) 4.0/3*Pi*radius*radius*radius // объем шара
#endif

void main()
{
  // 1. Радиус
  double r;

  // 2. Ввести радиус окружности
  cout << "r = ";
  cin >> r;

  // 3. Вычисление
  double result = RESULT(r);
  cout << "result = " << result << endl;
}

Результат

r = 3
result = 113.097
 
2. Директивы #ifdef, #ifndef. Условная компиляция

Директивы #ifdef и #ifndef обеспечивают еще один вид условной компиляции. Дословно значение директив можно определить следующим образом: «if defined» (если определено), «if not defined» (если не определено).

Общий вид директив #ifdef:

#ifdef macros_name
  operators_sequence
#endif

здесь

  • macros_name – имя макроса, которое проверяется на то, определено ли оно оператором #define. Если имя макроса определено, выполняется последовательность operators_sequence;
  • operators_sequence – некоторая последовательность.

Общий вид директивы #ifndef:

#ifndef macros_name
  operators_sequence
#endif

здесь

  • macros_name – имя макроса, которое может быть определено или не определено. Если имя макроса определено директивой #define, то последовательность operator_sequence не выполняется;
  • operators_sequence – последовательность операторов.

Пример.

#include <iostream>
using namespace std;

#define Is_Calc_Length // Нужно ли определять макрос Length?

// Если нужно определить макрос, вычисляющий расстояние
// между двумя точками, то определить его
#ifdef Is_Calc_Length
#define Length(x1, y1, x2, y2) sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))
#endif

// Если не нужно определять макрос Length, то вернуть 0
#ifndef Is_Calc_Length
#define Length(x1, y1, x2, y2) 0
#endif

void main()
{
  double x1, y1, x2, y2;
  cout << "x1 = "; cin >> x1;
  cout << "y1 = "; cin >> y1;
  cout << "x2 = "; cin >> x2;
  cout << "y2 = "; cin >> y2;

  double len = Length(x1, y1, x2, y2);
  cout << "len = " << len << endl;
}

Результат

x1 = 1
y1 = 2
x2 = 3
y2 = 5
len = 3.60555
 
3. Оператор defined. Определение, существует ли в программе макроимя

Оператор defined используется в сочетании с директивой #if. Сочетание этих двух элементов заменяет директиву #ifdef. Общая форма использования оператора defined имеет вид

defined macros_name

здесь

  • macros_name – имя макроса, которое проверяется на наличие.

Пример.

В примере демонстрируется оригинальный способ решений вычисления квадратного уравнения на основе предопределенных формул.

#include <iostream>
using namespace std;

// Оператор defined.
// Формули вычисления квадратного уравнения.
#define D(a, b, c) (b*b - 4*a*c)
#define X1(a, b, c) ((-b - sqrt(D(a,b,c))) / (2*a))
#define X2(a, b, c) ((-b + sqrt(D(a,b,c))) / (2*a))

void main()
{
  // Вычисление квадратного уравнения.
  // Проверка, есть ли формулы вычисления квадратного уравнения.
#if defined D
#if defined X1
#if defined X2
  // 1. Объявить переменные
  double a, b, c;

  // 2. Ввести коэффициенты
  cout << "a = "; cin >> a;
  cout << "b = "; cin >> b;
  cout << "c = "; cin >> c;

  // 3. Проверка, существует ли уравнение
  if (a == 0)
  {
    cout << "Incorrect value of a (a==0)." << endl;
    return;
  }

  // 4. Если дискриминант больше или равен 0, то произвести вычисление
  if (D(a, b, c) >= 0)
  {
    cout << "x1 = " << X1(a, b, c) << endl;
    cout << "x2 = " << X2(a, b, c) << endl;
  }
  else
    cout << "The equation has no roots." << endl;

#endif
#endif
#endif
}

Результат

a = -3
b = -4
c = 5
x1 = 0.7863
x2 = -2.11963
 

Связанные темы

  • Препроцессор. Общие сведения. Директивы препроцессора. Обзор. Директивы #define, #error, #include, #undef, #import, #using, #pragma, #line
  • Макросы. Директива #define. Примеры
 

Понравилась статья? Поделить с друзьями:

Это тоже интересно:

  • Инструкция котел стс family 1200
  • Инструкция котел лемакс ксг 10 инструкция
  • Инструкция как ухаживать за собакой кратко
  • Инструкция как ухаживать за собакой по пунктам
  • Инструкция как ухаживать за собакой 8 класс

  • Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии