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

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

Последнее обновление: 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;
}

Инструкции

ОО-нотация, разработанная в этой книге, императивна: вычисления специфицируются через команды (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, повторно запуская тело подпрограммы, работа которой была прервана.

Эта статья в основном знакомит с предварительно скомпилированными инструкциями, относящимися к условной компиляции на языке Си, включая определенные #define, #undef, #ifdef, #ifndef, #if, #elif, #else, #endif.
#define Определить макрос предварительной обработки
#undef Неопределить макрос

#if Компилировать условные команды в предварительной обработке, эквивалентно операторам if в синтаксисе C
#ifdef Определите, определен ли макрос. Если он определен, выполните следующую инструкцию
#ifndef В отличие от #ifdef, определите, является ли макрос неопределенным
#elif Если условия #if, #ifdef, #ifndef или предыдущие #elif не выполнены, выполняются операторы после #elif, что эквивалентно else-if в синтаксисе C
#else соответствует #if, #ifdef, #ifndef, если эти условия не выполняются, выполняются операторы после #else, что эквивалентно остальному в синтаксисе C
#endif #if, #ifdef, #ifndef Флаги окончания этих условных команд.
определено Используйте с #if, #elif, чтобы определить, определен ли макрос
Во-вторых, условная компиляция

Условная компиляция — это метод статической компиляции кода, основанный на фактически определенных макросах (какое-то условие). Вы можете определить условия компиляции на основе значения выражения или определения определенного макроса.

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

1 #ifndef ABCD_H
2 #define ABCD_H
3
4 // … some declaration codes
5
6 #endif // #ifndef ABCD_H

В файле реализации обычно есть похожие определения:
Скопировать код

1 #ifdef _DEBUG
2
3 // … do some operations
4
5 #endif
6
7 #ifdef _WIN32
8
9 // … use Win32 API
10
11 #endif

Скопировать код

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

#define Определение макроса предварительной обработки
#undef Неопределить макрос

#if Компилировать условные команды в предварительной обработке, эквивалентно операторам if в синтаксисе C
#ifdef Определите, определен ли макрос. Если он определен, выполните следующую инструкцию
#ifndef В отличие от #ifdef, определите, является ли макрос неопределенным
#elif Если условия #if, #ifdef, #ifndef или предыдущие #elif не выполнены, выполняются операторы после #elif, что эквивалентно else-if в синтаксисе C
#else соответствует #if, #ifdef, #ifndef, если эти условия не выполняются, выполняются операторы после #else, что эквивалентно остальному в синтаксисе C
#endif #if, #ifdef, #ifndef Флаги окончания этих условных команд.
определено Используйте с #if, #elif, чтобы определить, определен ли макрос
В-четвертых, примеры применения предварительно скомпилированных инструкций

  1. #define、#undef

Команда #define определяет макрос:
#define MACRO_NAME[(args)] [tokens[(opt)]]
MACRO_NAME, появляющийся после , будет заменен определенными токенами. Макросы могут принимать параметры, а следующие теги являются необязательными.

Определения макросов обычно делятся на объектные макросы и функциональные макросы с параметрами или без параметров.
Макрос объекта: Макросы без параметров называются «объектоподобными макросами». Макросы объектов в основном используются для определения констант и общих идентификаторов. Например:

// постоянное определение
#define MAX_LENGTH 100
// Общий логотип, макрос вывода журнала
#define SLog printf
// предварительно скомпилированный макрос
#define _DEBUG

Макрос функции: Макрос с параметрами. Использование макросов может повысить эффективность выполнения кода: вызовы подпрограмм должны быть вытолкнуты из стека, и этот процесс будет потреблять много вычислительных ресурсов ЦП, если он будет слишком частым. Следовательно, некоторый код с небольшим количеством кода, но часто выполняемый, улучшит эффективность выполнения кода, если он будет реализован с помощью макрокоманд параметров. Тем не менее, большинство программ на C ++ не рекомендуют использовать макросы функций, которые трудно отладить. Попробуйте вместо этого использовать встроенный C ++. Например:

// минимальная функция
#define MIN(a,b) ((a)>(b)? (a):(b))
// безопасно отключаем функцию памяти
#define SAFE_DELETE§ {if(NULL!=p){delete p; p = NULL;}}

#undef может отменить определение макроса, соответствующее #define.
2. defined

определенный используется, чтобы проверить, определен ли макрос. define (name): Возвращает 1, если макрос определен, в противном случае возвращает 0.
Он используется в сочетании с #if, #elif, #else для определения, определен ли макрос. На первый взгляд он кажется избыточным, поскольку уже есть #ifdef и #ifndef. определено может использоваться для объявления нескольких условий суждения в суждении суждения; #ifdef и #ifndef поддерживают только оценку, определен ли макрос

#if defined(VAX) && defined(UNIX) && !defined(DEBUG)

В отличие от #if, #elif, #else, #ifdef, #ifndef и определенные тестовые макросы могут быть как объектными, так и функциональными макросами.
3. #ifdef、#ifndef、#else、#endif

Относительно часто используемые предварительно скомпилированные инструкции в условной компиляции. Шаблон выглядит следующим образом:
Скопировать код

#ifdef ABC
// … codes while definded ABC
#elif (CODE_VERSION > 2)
// … codes while CODE_VERSION > 2
#else
// … remained cases
#endif // #ifdef ABC

Скопировать код

#ifdef используется для определения того, определен ли макрос. Он противоположен #ifndef. Оба поддерживают только определение того, был ли определен один макрос. В вышеприведенном примере они взаимозаменяемы. Если предварительная компиляция с несколькими условиями не требуется, #elif и #else в приведенном выше примере могут быть опущены.
4. #if、#elif、#else、#endif

#if поддерживает оценку наличия нескольких макросов одновременно и может использоваться с константными выражениями. Общие форматы следующие:
Скопировать код

#if Постоянное выражение 1
// … some codes
#elif константное выражение 2
// … other codes
#elif константное выражение 3
// …

#else
// … statement
#endif

Скопировать код

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

#if MACRO_NON_DEFINED // эквивалентно

#if 0

Вы должны избегать использования #if при определении, определен ли макрос, потому что значение макроса может быть определено как 0. Вместо этого используйте #ifdef или #ifndef.
Примечание. Макросы после #if и #elif могут быть только объектными макросами. Если макрос не определен или макрос является функциональным макросом, у компилятора может появиться предупреждение о том, что соответствующий макрос не определен.
5. Резюме

Эта статья в основном представляет предварительно скомпилированные инструкции на языке Си. Целью написания этой статьи является прояснение связанных концептуальных вызовов и поиск наиболее подходящих инструкций и форматов при их использовании в последующей предварительной компиляции. Например, выполняется одновременная предварительная компиляция нескольких определений макросов, предварительная компиляция с несколькими ветвями и взаимодействие команд #elif и #else.

Во-первых, если условная компиляция, выберите компиляцию
(1)
#if ()
//*******
#endif
(2)
#if ()
//******
#else
//******
#endif
(3)
#if ()
//******
#elif ()
//******
#elif ()
//******
#endif
2. Обратите внимание, что вы не можете добавить здесь «()», в противном случае скобки также будут обрабатываться как строка определения макроса.
#define DBG
3.
#define A C(0、1…)
4. Обратите внимание, что здесь нельзя добавить «()», в противном случае скобки также будут рассматриваться как строка, определенная макросом.
#define DBG
#undefine DBG
5. Обратите внимание, что здесь нельзя добавить «()», в противном случае скобки также будут обрабатываться как строка, определенная макросом.
(1)
#ifdef DBG
#define UNDBG
#define UNDBG1
#endif
(2)
#ifundef DBG
#define UNDBG
#define UNDBG
#endif

6. Стандартная форма «()» должна быть добавлена ​​в сложных условиях «&& и ||», таких как: # if (define (DBG)) || (define (DBG1))
(1)
# if define DBG || define DBG1 || define DBG2
//******
#endif
(2)
#if !define DBG || !define DBG2
//******
#endif

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

                     Рассмотрим следующий пример:

#include<stdio.h>
#define BB
#ifdef AA
#define HELLO “hello world”
#elif BB
#define HELLO “hello CC”
#endifint main()
{
printf(«%sn»,HELLO);
return 1;
}

        Если вы думаете, что этот отпечаток будет привет CC, то вы сделали ту же ошибку, что и я. Если вы скомпилируете с помощью gcc -E hello.c -o hello.i, (это предварительно скомпилированная команда, которая будет описана ниже.) Появится: error: #if без выражения. Причина в том, что хотя BB определен, он определен как нулевое значение и не может быть помещен после #elif. Потому что #elif не только проверяет, определен ли следующий макрос, но и его значение. Но #ifdef просто проверяет, определен ли следующий макрос, независимо от его значения. Читатель может поставить #define
         Измените BB на #define AA и попробуйте, результат напечатает привет мир.
         Если читатель заинтересован, вы также можете изменить #define BB на #define BB 0. Попробовать его. В это время можно выполнить предварительную компиляцию с помощью gcc -E hello.c -o hello.i, но процесс компиляции не будет работать, потому что # elif 0 - false, HELLO не определено.

Эти макросы предназначены для условной компиляции. В общем случае все строки в исходной программе компилируются. Однако иногда желательно компилировать часть контента только при соблюдении определенных условий, то есть указывать условия компиляции для части контента. Иногда желательно скомпилировать набор операторов, когда определенное условие выполнено, и скомпилировать другой набор операторов, когда условие не выполнено.
Наиболее распространенная форма команды условной компиляции :
#ifdef идентификатор
Сеть 1
#else
Сеть 2
#endif

Его роль заключается в следующем: когда идентификатор был определен (обычно определяется командой #define), программный сегмент 1 компилируется, в противном случае программный сегмент 2 компилируется.
 Часть #else также может быть опущена, то есть:
#ifdef
 Блок 1
#denif

 «Программа» здесь может быть группой операторов или командной строкой. Эта условная компиляция может улучшить универсальность исходных программ на Си. Если исходная программа на С работает в разных компьютерных системах, и разные компьютеры имеют определенные различия. Например, у нас есть тип данных. На платформе Windows мы должны использовать тип long, а на других платформах мы должны использовать тип float. Это часто требует необходимых изменений в исходной программе, что уменьшает общность программы. Может быть скомпилирован со следующими условиями:
#ifdef WINDOWS
#define MYTYPE long
#else
#define MYTYPE float
#endif

 Если вы компилируете программу в Windows, вы можете добавить
#define WINDOWS

 Это скомпилирует следующую командную строку:
#define MYTYPE long

 Если перед этим набором команд условной компиляции появилась следующая командная строка:
#define WINDOWS 0

 MYTYPE в предварительно скомпилированной программе заменяется на float. Таким образом, исходная программа может использоваться в различных типах компьютерных систем без каких-либо изменений. Конечно, приведенное выше описание является лишь простым случаем, и другие условные компиляции могут быть разработаны на основе этой идеи.
 Например, при отладке программы вы часто хотите вывести некоторую требуемую информацию и больше не выводить эту информацию после завершения отладки. Следующие разделы условной компиляции могут быть вставлены в исходную программу:
#ifdef DEBUG
print ("device_open(%p)n", file);
#endif

 Если перед вами следующая командная строка:
#define DEBUG

 Когда программа работает, значение указателя файла выводится для отладки и анализа. После отладки просто удалите командную строку define. Некоторые люди могут подумать, что этого можно достичь без условной компиляции, то есть добавить пакет операторов printf во время отладки и удалить операторы printf один за другим после отладки. Действительно, это возможно. Однако, когда во время отладки добавляется больше операторов printf, рабочая нагрузка модификации очень велика. При условной компиляции вам не нужно удалять операторы printf одну за другой. Вам нужно только удалить предыдущую команду "#define DEBUG". В настоящее время все разделы условной компиляции, которые используют DEBUG в качестве идентификатора, сделают операторы printf в нем недействительными. Он играет роль унифицированного контроля, как «переключатель».
 Иногда это также принимает следующую форму:
 #ifndef идентификатор
 Блок 1
#else
 Блок 2
#endif

 Просто первая строка отличается от первой формы: замените «ifdef» на «ifndef». Его роль состоит в том, чтобы скомпилировать сегмент 1, если идентификатор не определен, иначе скомпилировать сегмент 2. Эта форма имеет эффект, противоположный первому.
 Использование двух вышеуказанных форм аналогично, вы можете выбрать одну из них в соответствии с вашими потребностями, в зависимости от вашего удобства.
 Существует также форма, которая является выражением после #if вместо простого идентификатора:
 #if выражение
 Блок 1
#else
 Блок 2
#endif

 Его роль состоит в том, чтобы скомпилировать сегмент 1, когда указанное значение выражения является истинным (ненулевым), в противном случае он компилирует сегмент 2. Определенные условия могут быть заданы заранее, чтобы программа выполняла разные функции в разных условиях.
 Например: введите строку буквенных символов и установите условную компиляцию в соответствии с вашими потребностями, чтобы она могла заменить все буквы на заглавные или строчные.
#define LETTER 1
main()
{
char str[20]="C Language",c;
int i="0";
while((c=str[i])!=''){
i++;
#if LETTER
if(c>='a'&&c<='z') c="c-32";
#else
if(c>='A'&&c<='Z') c="c"+32;
#endif
printf("%c",c);
}
}

 Результат: C ЯЗЫК
 Теперь давайте определим LETTER как 1, чтобы при предварительной обработке команды условной компиляции, поскольку LETTER имеет значение true (отличное от нуля), первый оператор if компилируется, а строчные буквы делаются заглавными при выполнении. Если вы измените первую строку программы на:
#define LETTER 0

 Во время предварительной обработки второй оператор if компилируется, чтобы заглавные буквы стали строчными (разница между кодом ASCII заглавной буквы и соответствующей строчной буквой составляет 32). Операция в это время:
c language
 Некоторые люди могут спросить: Если вы можете использовать оператор if без команд условной компиляции, вы также можете выполнить требования. Каковы преимущества использования команд условной компиляции? Действительно, эта проблема может быть решена без условной компиляции, но целевая программа длинная (поскольку все операторы компилируются), и условная компиляция может уменьшить количество скомпилированных операторов, тем самым уменьшая длину цели. Когда имеется много разделов условной компиляции, длина целевой программы может быть значительно уменьшена.

О чудесном использовании #ifdef в разработке программного обеспечения

Автор занимается разработкой и обслуживанием определенного прикладного программного обеспечения в среде UNIX. Пользователи распределены по всей стране. Основные функции, которые требуются каждому пользователю, одинаковы. Однако некоторые функции должны постоянно обновляться по мере изменения требований. Обновление пользователей по всей стране очень сложно, и мы просто используем электронную почту для отправки исправлений пользователям. Эти исправления основаны на наборе программного обеспечения, которое постоянно модифицируется и расширяется и пишется различными Файл логотипа передается в разные модули. Хотя объем программы постоянно расширяется, он никак не влияет на функции старых пользователей, что в основном связано с ролью # ifdef / # else / # endif в программе на Си.

В основном мы используем следующие методы, предполагая, что мы определили #ifdef DEBUG и #ifdef TEST в заголовке программы:

1. Используйте # ifdef / # endif, чтобы включить программный функциональный модуль для предоставления функции пользователю.

Определите #ifdef HNLD в заголовке программы:

#ifdef HNLD

include»n166_hn.c»

#endif

Если эта функция не может быть предоставлена ​​другим пользователям, вы можете подчеркнуть HNLD заголовка перед компиляцией.

2. Добавьте отметку перед каждой подпрограммой, чтобы отслеживать выполнение программы.

#ifdef DEBUG

printf(» Now is in hunan !»);

#endif

3. Избегайте аппаратных ограничений. Иногда аппаратные средства некоторых конкретных прикладных сред отличаются, но из-за условий локального отсутствия такого оборудования, поэтому обходят аппаратное обеспечение и пишут ожидаемые результаты напрямую. Конкретный подход:

#ifndef TEST

i=dial();

// Обходим этот оператор, когда выполняется отладка программы

#else

i=0;

#endif

После отладки замаскируйте определение TEST и перекомпилируйте, а затем отправьте его пользователю.

ifdef #ifndef и другое использование (очередь)

#Ifndef в заголовке является ключевым моментом. Например, у вас есть два файла C, каждый из которых содержит один и тот же заголовочный файл. При компиляции эти два файла C должны быть скомпилированы в исполняемый файл вместе, поэтому возникает проблема и возникает большое количество конфликтов объявлений.

Лучше поместить содержимое заголовочных файлов в #ifndef и #endif. Независимо от того, ссылаются ли на ваши заголовочные файлы несколько файлов, вы должны добавить это. Общий формат таков:

#ifndef <идентификация>
#define <идентификация>


#endif

Теория <идентификации> может быть произвольно названа, но эта «идентификация» каждого заголовочного файла должна быть уникальной. Правила именования логотипа — это, как правило, все имена файлов в верхнем регистре, подчеркнутые до и после, и также подчеркнут символ «.» В имени файла, например: stdio.h

#ifndef STDIO_H
#define STDIO_H

#endif

2. Проблемы с определением переменных в #ifndef (обычно не определяются в #ifndef).

#ifndef AAA
#define AAA

int i;

#endif
есть определение переменной
При линковке в vc я обнаружил ошибку повторного определения i и успешно скомпилировал в c.

Вывод:

(1). Когда вы впервые используете файл .cpp этого заголовка для генерации .obj, в нем определяется int i. Когда другой .cpp использует это для [индивидуальной] генерации .obj снова, int i снова Определение, а затем два объекта включены другим .cpp также включают этот заголовок, соединенный вместе, будут повторяющиеся определения.

(2). После изменения расширения файла исходной программы на .c VC компилирует исходную программу в соответствии с синтаксисом языка C, а не C ++. В языке C, если встречается несколько int i, один из них автоматически считается определением, а другой — объявлением.

(3). Результаты соединения языка C и языка C ++ различны, возможно (догадывается), что при компиляции язык C ++ будет глобальным
Переменная по умолчанию имеет сильный символ, поэтому произошла ошибка соединения. Язык C определяет, будет ли он инициализирован в зависимости от того, инициализирован он или нет. (Ссылка)

Решение:

(1). Измените расширение файла исходной программы на .c.

(2). Рекомендуемое решение:
.h объявляет только extern int i; определено в .cpp

<x.h>
#ifndef X_H
#define X_H
extern int i;
#endif //X_H
<x.c>
int i;

Обратите внимание на проблему:

(1). Переменные не должны быть определены в .h файлах.

 

Препроцессор — это специальная программа, являющаяся частью компилятора языка Си. Она предназначена для предварительной обработки текста программы. Препроцессор позволяет включать в текст программы файлы и вводить макроопределения.
Работа препроцессора осуществляется с помощью специальных директив (указаний). Они отмечаются знаком решетка #. По окончании строк, обозначающих директивы в языке Си, точку с запятой можно не ставить.

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

#include — вставляет текст из указанного файла
#define — задаёт макроопределение (макрос) или символическую константу
#undef — отменяет предыдущее определение
#if — осуществляет условную компиляцию при истинности константного выражения
#ifdef — осуществляет условную компиляцию при определённости символической константы
#ifndef — осуществляет условную компиляцию при неопределённости символической константы
#else — ветка условной компиляции при ложности выражения
#elif — ветка условной компиляции, образуемая слиянием else и if
#endif — конец ветки условной компиляции
#line — препроцессор изменяет номер текущей строки и имя компилируемого файла
#error — выдача диагностического сообщения
#pragma — действие, зависящее от конкретной реализации компилятора.

Директива #include

Директива #include позволяет включать в текст программы указанный файл. Если заголовочный файл содержит описание библиотечных функций и находится в папке компилятора, он заключается в угловые скобки <>.
Если файл находится в текущем каталоге проекта, он указывается в кавычках «». Для файла, находящегося в другом каталоге необходимо в кавычках указать полный путь.

#include <stdio.h>
#include «func.c»

Директива #define позволяет вводить в текст программы константы и макроопределения.
Общая форма записи

#define Идентификатор Замена

Поля Идентификатор и Замена разделяются одним или несколькими пробелами.
Директива #define указывает компилятору, что нужно подставить строку, определенную аргументом Замена, вместо каждого аргумента Идентификатор в исходном файле. Идентификатор не заменяется, если он находится в комментарии, в строке или как часть более длинного идентификатора.

1
2
3
4
5
6
7
8

#include <stdio.h>
#define A 3
int main()
{
  printf(«%d + %d = %d», A, A, A+A); // 3 + 3 = 6
  getchar();
  return 0;
}

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

  • U или u представляет целую константу в беззнаковой форме (unsigned);
  • F (или f) позволяет описать вещественную константу типа float;
  • L (или l) позволяет выделить целой константе 8 байт (long int);
  • L (или l) позволяет описать вещественную константу типа long double

#define A 280U   // unsigned int
#define B 280LU  // unsigned long int
#define C 280    // int (long int)
#define D 280L   // long int
#define K 28.0   // double
#define L 28.0F  // float
#define M 28.0L  // long double

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

идентификатор(аргумент1, …, агрументn)

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

Пример на Си: Вычисление синуса угла

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define PI 3.14159265
#define SIN(x) sin(PI*x/180)
int main()
{
  int c;
  system(«chcp 1251»);
  system(«cls»);
  printf(«Введите угол в градусах: «);
  scanf(«%d», &c);
  printf(«sin(%d)=%lf», c, SIN(c));
  getchar(); getchar();
  return 0;
}

Результат выполнения
Директива define

Отличием таких макроопределений от функций в языке Си является то, что на этапе компиляции каждое вхождение идентификатора замещается соответствующим кодом. Таким образом, программа может иметь несколько копий одного и того же кода, соответствующего идентификатору. В случае работы с функциями программа будет содержать 1 экземпляр кода, реализующий указанную функцию, и каждый раз при обращении к функции ей будет передано управление.
Отменить макроопределение можно с помощью директивы #undef.

Однако при использовании таких макроопределений следует соблюдать осторожность, например

1
2
3
4
5
6
7
8
9
10
11
12
13

#include <stdio.h>
#define sum(A,B) A+B
int main()
{
  int a, b, c, d;
  a = 3; b = 5;
  c = (a + b) * 2; // c = (a + b)*2
  d = sum(a, b) * 2; // d = a + b*2;
  printf(» a = %dn b = %dn», a, b);
  printf(» c = %d n d = %d n», c, d);
  getchar();
  return 0;
}

Результат выполнения:
Использование макроопределений define
По умолчанию текст макроопределения должен размещаться на одной строке. Если требуется перенести текст макроопределения на новую строку, то в конце текущей строки ставится символ «обратный слеш» — .

1
2
3
4
5
6
7
8
9
10
11
12
13
14

#include <stdio.h>
#define sum(A,B) A + 
                 B
int main()
{
  int a, b, c, d;
  a = 3; b = 5;
  c = (a + b) * 2; // c = (a + b)*2
  d = sum(a, b) * 2; // d = a + b*2;
  printf(» a = %dn b = %dn», a, b);
  printf(» c = %d n d = %d n», c, d);
  getchar();
  return 0;
}

Кроме того, директива #define позволяет замещать часть идентификатора. Для указания замещаемой части используется ##.

1
2
3
4
5
6
7
8
9

#include <stdio.h>
#define SUM(x,y) (a##x + a##y)
int main()
{
  int a1 = 5, a2 = 3;
  printf(«%d», SUM(1, 2)); // (a1 + a2)
  getchar();
  return 0;
}

Результат выполнения:
Использование ## в директиве #define

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

Директивы #if или #ifdef/#ifndef вместе с директивами #elif, #else и #endif управляют компиляцией частей исходного файла.
Если указанное выражение после #if имеет ненулевое значение, в записи преобразования сохраняется группа строк, следующая сразу за директивой #if. Синтаксис условной директивы следующий:

1
2
3
4
5
6
7

#if константное выражение
   группа операций
#elif константное выражение
   группа операций
#else
   группа операций
#endif

Отличие директив  #ifdef/#ifndef заключается в том, что константное выражение может быть задано только с помощью #define.

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

Пример

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

#include <stdio.h>
#include <stdlib.h>
#define P 2
int main()
{
  system(«chcp 1251»);
  system(«cls»);
#if P==1
  printf(«Выполняется ветка 1»);
#elif P==2
  printf(«Выполняется ветка 2, P=%d», P);
#else
  printf(«Выполняется другая ветка, P=%d», P);
#endif
  getchar();
  return 0;
}

Результат выполнения
Условная компиляция

Назад: Язык Си

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. Mastering Software Variability with FeatureIDE. 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. Archived from the original on 2022-11-28. Retrieved 2023-01-21.

Понравилась статья? Поделить с друзьями:
  • Розарт 10 мг инструкция по применению цена отзывы аналоги дешевые
  • Заявление на увольнение конфликт с руководством
  • Руководство организации объединенных наций
  • Компливит актив инструкция по применению взрослым
  • Золидрованная кислота инструкция по применению цена отзывы