Препроцессор — это специальная программа, являющаяся частью компилятора языка Си. Она предназначена для предварительной обработки текста программы. Препроцессор позволяет включать в текст программы файлы и вводить макроопределения.
Работа препроцессора осуществляется с помощью специальных директив (указаний). Они отмечаются знаком решетка #. По окончании строк, обозначающих директивы в языке Си, точку с запятой можно не ставить.
Основные директивы препроцессора
#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;
}
Результат выполнения
Отличием таких макроопределений от функций в языке Си является то, что на этапе компиляции каждое вхождение идентификатора замещается соответствующим кодом. Таким образом, программа может иметь несколько копий одного и того же кода, соответствующего идентификатору. В случае работы с функциями программа будет содержать 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;
}
Результат выполнения:
По умолчанию текст макроопределения должен размещаться на одной строке. Если требуется перенести текст макроопределения на новую строку, то в конце текущей строки ставится символ «обратный слеш» — .
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;
}
Результат выполнения:
Условная компиляция
Директивы #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;
}
Результат выполнения
Назад: Язык Си
Условная компиляция
Последнее обновление: 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 корректен, только если он удовлетворяет следующим условиям:
- Компонент, стоящий после первой точки, q1, должен быть доступен в классе C.
- В вызове с многоточием, каждый компонент после второй точки, то есть каждое 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, повторно запуская тело подпрограммы, работа которой была прервана.
Search code, repositories, users, issues, pull requests…
Provide feedback
Saved searches
Use saved searches to filter your results more quickly
Sign up
│
Deutsch (de) │
English (en) │
suomi (fi) │
français (fr) │
русский (ru) │
Что такое условная компиляция?
Условная компиляция — это компиляция или пропуск части исходного кода в зависимости от того, существует условие или нет.
Функции, которые делают это возможным в большинстве компилируемых языков, называются директивами времени компиляции. Директивы времени компиляции позволяют компилировать блок кода на основе наличия или отсутствия условия во время компиляции. Они являются частью директив компилятора.
Они могут использоваться для различных целей, таких как:
- изоляция кода для конкретной платформы
- выбор естественного языка
- лицензирование частей с открытым и закрытым исходным кодом
- изоляция экспериментального кода
- версия компилятора
- версия библиотеки
- и т.д. и т.п.
Free Pascal поддерживает четыре различных стиля условной компиляции:
- Turbo Pascal и ранние директивы стиля Delphi
- директивы стиля Mac Pascal
- Современные директивы в стиле Free Pascal и Delphi
- Макросы времени компиляции
Примечание: здесь синтаксис не чувствителен к регистру, поскольку соответствует всему синтаксису Pascal. Мы будем использовать как строчные, так и прописные примеры. Мы покажем вам разницу между режимами и как их эффективно использовать.
Директивы в стиле Turbo Pascal
Директивами в стиле Turbo Pascal являются {$DEFINE}
, {$IFDEF}
, {$ENDIF}
, {$IFNDEF}
, {$IFOPT}
, {$ELSE}
, {$ELSEIF}
и {$UNDEF}
.
Мы опишем директивы в контексте стиля. Некоторые определения имеют расширенное значение в другом стиле.
Это означает, что в дальнейшем мы можем расширить значение некоторых директив, таких как, например, {$DEFINE}
, в контексте макросов.
$define
Директива {$DEFINE}
просто объявляет символ, который мы позже можем использовать для условной компиляции:
{$DEFINE name} //Это объявляет символ под названием "name"
Обратите внимание, что вы также можете объявить символ из командной строки или , например, -dDEBUG IDE, который будет являться эквивалентом командной строки
в исходном коде.
$undef
Директива {$UNDEF}
отменяет объявление ранее определенного символа. Вот пример, который автор использует на практике:
// Некоторый старый исходный код, "загрязненый" обилием {$IFDEF FPC}, который больше // не нуждается в зависимости от версии Delphi, с которой он должен быть совместим. // Я всегда проверяю это, пробуя это поверх программы или модуля: {$IFDEF FPC} {$MODE DELPHI} {$UNDEF FPC} {$DEFINE VER150} // код теперь будет компилироваться так, как если бы это был Delphi 7, при условии, что исходный код Delphi действительно был написан для Delphi 7 и выше. {$ENDIF}
$ifdef и $endif
Самый простой способ объявить блок условного кода так:
unit cross; {$IFDEF FPC}{$MODE DELPHI}{$ENDIF}
Приведенный выше пример довольно распространен для исходного кода, который должен компилироваться как в Delphi, так и в Free Pascal.
Если компилятором является Delphi, то ничего не делается, но если компилятором является Free Pascal, он переключит Free Pascal для компиляции и использования режима синтаксиса Delphi.
Этот условный символ «FPC» объявлен в системе — там их длинный список. Синтаксис блоков {$IFDEF}
и {$ENDIF}
является симметричным: каждый {$IFDEF}
имеет свой собственный {$ENDIF}
.
Чтобы помочь вам распознать соответствующие блоки, вы можете использовать, например, отступ, но вы также можете использовать функцию комментариев:
{$IFDEF FPC эта часть специфична для Free Pascal} // некий Free Pascal специфичный код {$ENDIF Free Pascal специфичный код}
Предупреждение: Эта особенность комментариев часто не совсем понятна. Некоторые люди — как и в более старой версии этой записи вики — предположили, что вы можете вкладывать {$IFDEF}
, потому что компилятор, кажется, принимает синтаксис. Но первое неверно, а второе верно: да, компилятор принимает приведенный ниже синтаксис, но это не вложенное {$IFDEF}
, а одиночное условие {$IFDEF}
, а остальное — комментарий!
Приведенный ниже код выполнит writeln тогда и только тогда, когда будет объявлен {$define red}
. В этом примере {$ifdef blue}
является комментарием! Даже если {$define blue}
допустим.
// программа формально будет скомпилирована, но полностью неработоспособна {$define blue} begin {$ifdef red or $ifdef blue}// все после red - это комментарий writeln ('red or blue'); // этот код никогда не выполнится {$endif red or blue} // все, что объявлено после $endif, является комментарием. end.
$ifndef
Это противоположно {$IFDEF}
, и ниже лежащий код выполнится, если объявленное условие не выполняется. Простой пример:
{$IFNDEF FPC эта часть не для Free Pascal} // некий конкретный код, который Free Pascal не должен компилировать {$ENDIF код для других компиляторов, кроме Free Pascal}
$else и $elseif
{$ELSE}
используется для компиляции кода, который не принадлежит блоку кода, который объявлен соответствующим {$IFDEF}
. Он также действителен в контексте {$IFOPT}
, {$IF}
или {$IFC}
, которые мы обсудим позже.
{$IFDEF red} writeln('Red объявлен'); {$ELSE no red} {$IFDEF blue} writeln('Blue объявлен, а red - не объявлен'); {$ELSE no blue} writeln('Ни red, ни blue не объявлены'); {$ENDIF blue} {$ENDIF red}
Такие вложенные условные выражения, написанные в приведенном выше синтаксисе, могут стать очень путанными и нечитаемыми. К счастью, мы можем упростить это, используя {$ELSEIF}
. Код ниже является расширенным эквивалентом первого примера:
{$IF Defined(red)} writeln('Red объявлен'); {$ELSEIF Defined(blue)} writeln('Blue объявлен'); {$ELSEIF Defined(green)} writeln('Green объявлен'); {$ELSE} writeln('Ни red, ни blue, ни green не объявлены. Должен быть black...или что-нибудь еще...'); {$ENDIF}
Как вы можете видеть, это гораздо более читабельно.
$ifopt
С помощью {$IFOPT}
мы можем проверить, задана ли определенная опция компиляции.
Из руководства по программированию:
{$IFOPT switch}
скомпилирует текст, который следует за ним, если переключатель switch в данный момент будет находиться в указанном состоянии. Если он не находится в указанном состоянии, то компиляция продолжится после соответствующей директивы{$ELSE}
или{$ENDIF}
.
Например:
{$IFOPT M+} Writeln('Compiled with type information'); {$ENDIF}
Скомпилирует оператор Writeln, только если включена генерация информации о типе.
Примечание: директива {$IFOPT}
принимает только короткие опции, т.е. {$IFOPT TYPEINFO}
не будет принята.
Обычно этот пример используется для проверки, задан ли режим DEBUG:
{$IFOPT D+}{$NOTE debug mode is active}{$ENDIF}
Такие определения также могут находиться в конфигурационных файлах, таких как fpc.cfg, которые также содержат полное объяснение того, как использовать:
# ---------------------- # Defines (preprocessor) # ---------------------- # # nested #IFNDEF, #IFDEF, #ENDIF, #ELSE, #DEFINE, #UNDEF are allowed # # -d is the same as #DEFINE # -u is the same as #UNDEF # # # Some examples (for switches see below, and the -? help pages) # # Try compiling with the -dRELEASE or -dDEBUG on the command line # # For a release compile with optimizes and strip debug info #IFDEF RELEASE -O2 -Xs #WRITE Compiling Release Version #ENDIF
Как не нужно делать
Что не так с этим кодом? Вы можете это заметить?
var MyFilesize: {$ifdef Win32} Cardinal {$else} int64 {$endif}
Вот ответ:
- Free Pascal компилируется для большего количества типов процессоров, чем 32 и 64 bit, например, для 8 и 16 бит.
- на большинстве 64-битных платформ максимальный размер файла — это QWord, а не Int64.
Этот программист попал в ловушку, которая распространена: если вы используете объявление, убедитесь, что ваша логика надежна. В противном случае такой код может легко вызвать несчастные случаи. Компилятор не будет ловить ваши логические ошибки!
Всегда хорошо осознавать такие вещи, тем более что такие вещи легко исправить.
var MyFilesize: {$if defined(Win32)} Cardinal {$elseif defined(Win64)} Qword; {$else} {$error этот код написан для win32 или win64} {$endif}
Помимо этого, есть решение для этого конкретного примера, которое вообще не использует условные выражения:
var MyFilesize:NativeUint;
Что не так с этим кодом? Вы можете это заметить?
{$IFDEF BLUE AND $IFDEF RED} Form1.Color := clYellow; {$ENDIF} {$IFNDEF RED AND $IFNDEF BLUE} Form1.Color := clAqua; {$ENDIF}
Вот ответ:
- Ну, я уже написал комментарий, который предупредил вас .. так что посмотрите на предупреждение …. Вы должны быть в состоянии определить это …
- Директивы компилятора переопределяют компилятор … будьте осторожны с этим топором Юджина.
Directives, definitions and conditionals definitions |
---|
global compiler directives • local compiler directives
Conditional Compiler Options • Conditional compilation • Macros and Conditionals • Platform defines |