$OverFlowChecks — Директива компилятора Delphi


Delphi. Условная компиляция. Краткий справочник

Условная компиляция предназначена для управления компиляцией программного кода. В Delphi имеется несколько способов её реализации, но в основном используются только четыре.

В этой реализации условная директива <$IFDEF …>проверяет, был ли определён указанный в ней символ условной компиляции. Если да, то код программы между директивами <$IFDEF …>и <$ENDIF>компилируется. В противном случае, код между этими двумя директивами компилироваться не будет.

Эта реализация отличается от предыдущей наличием директивы <$ELSE>. В этом случае, если условный оператор определён, будет компилироваться код программы между директивами <$IFDEF>и <$ELSE>. В противном случае, будет компилироваться код программы между директивами <$ELSE>и <$ENDIF>.

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

Как определить символ условной компиляции? Для этого также существует два способа.

Первый – использование директивы

Второй способ. Определить его в свойствах проекта. Для этого в меню Project нужно выбрать пункт Options и в поле Conditional defines ввести символ условной компиляции.

Третий и четвёртый способы являются аналогией первого и второго соответственно. Отличие в том, что используется условная директива <$IFNDEF>. Эта директива проверяет, что указанный символ не определён. То есть, является прямой противоположностью директивы <$IFDEF>.

В случае <$IFNDEF>, код между этой директивой и <$ENDIF>либо <$ELSE>будет скомпилирован, только если указанный в ней символ условной компиляции не определён. В остальном, принцип реализации аналогичен приведённому для директивы <$IFDEF>:

В Delphi уже имеется ряд символов условной компиляции. Вот их «классический» набор.

Значение
MSWINDOWS Код компилируется только в ОС Windows
WIN32 То же самое, только код компилируется лишь для 32-разядной ОС.
LINUX Был введён в Delphi 6 вместе с MSWINDOWS для определения платформы, для которой необходимо компилировать тот или иной код.Так как компиляция для Linux в Delphi до сих пор отсутствует, то использование этого символа в программах не имеет смысла.
CONSOLE Консольное приложение
CPU386 Компиляция для процессоров типа i386 или более современных. Так как речь идёт о процессорах типа i386, использование данного символа уже давно потеряло актуальность.
CONDITIONAL EXPRESSIONS Проверяет использование директив $IF

Также в число «классических» входит и символ VERxxx, который указывает конкретную версию компилятора. По сути, это целое семейство символов условной компиляции, в которое с выходом новой версии Delphi добавляется новый символ соответствующий этой версии. Ниже представлена таблица соответствия версий Delphi символам условной компиляции VERxxx (материал взят с Исходники.ру).

Символ условной компиляции
1 VER80
2 VER90
3 VER100
4 VER120
5 VER130
6 VER140
7 VER150
8 VER160
2005 VER170
2006 VER180
2007* VER180 или VER185
2009 VER200
2010 VER210
XE VER220
XE2 VER230
XE3 VER240
XE4 VER250
XE5 VER260
XE6 VER270
XE7 VER280
XE8 VER290
10 Seatle VER300
10.1 Berlin VER310
10.2 Tokyo VER320

*-версии 2006 и 2007 совместимы между собой на уровне двоичного кода. Поэтому, для поддержки обеих версий следует использовать символ VER180. Если необходима поддержка только версии 2007, необходимо использовать символ VER185.

После выхода в 2011 году кроссплатформенной библиотеки FireMonkey, были введены новые символы условной компиляции для MacOS и iOS. А, в 2013 году с появлением поддержки Android был введён и специальный символ условной компиляции для этой ОС. Ниже представлен перечень этих символов.

Символ условной компиляции
MacOS MACOS
iOS IOS
Android ANDROID

Условная компиляция на примере кроссплатформенного приложения:

$OverFlowChecks — Директива компилятора Delphi

Группа: Пользователи
Сообщений: 4 825
Пол: Мужской
Реальное имя: Олег

Директивы компилятора
Почему-то мало кто пользуется директивами компилятора в полном масштабе.
Еще <$M> <$N> <$E>приходится видеть, но вот такие:
<$ifdef>. <$else>. — почти никогда; несмотря на то, что они открывают довольно широкие возможности для программиста, а именно: позволяют писать код, успешно компилируемый на разных компиляторах Паскаля, в том числе и 32 битных, включая FPC и TMT.

Еще одно удобство на мой взгяд — широкие возможности при отладке приложений.

Общий синтаксис этих команд таков:
— установить условный_символ.
— код следующий после этой директивы компилируется только в том случае, если условный_символ был установлен.
— эта директива обозначает начало альтернативного участка кода.
— ограничивает действие директив <$ifdef>и <$else>.

Режимы компиляции. Отладка.
Предположим, что в программе необходимо на этапе отладки выводить какие-то данные для тестирования алгоритма. Можно, конечно, в окончательном варианте кода закомментировать такие отладочные выводы, или удалить, но лучше заключить «отладочные операторы» в такую конструкцию, с использованием директив:

Таким образом, при работе программы на экран будет выведено значение «2». Если отладку надо отменить, мы можем убрать символ «$» из директивы <$define debug>— и теперь это будет просто коментарий, и следовательно на экран ничего не будет выведено.

Универсальность кода
Хорошо, когда мы берем старый код из TP и он компилируется, и программа правильно работает в компиляторе постарше.
А почему бы не реализовать совместимость наоборот или напрямую? Часто при компиляции, возникают ошибки только из-за того, что код оптимизирован под конкретный компилятор, например под BP, и в TP отказывается компилироваться.
Выходом из такой ситуации является правильное написание кода, а именно отладка кода под различные компиляторы (конечно если это требуется). Для примера рассмотрим программу для построения графиков некоторых функций (в полярной системе координат) скачать архив

Этот код можно скомпилировать вот в этих компиляторах: FPC(target: Win32, DOS), TP7, BP7, BPW (target:real, protected mode), TMT.
Неплохо, правда? И везде он будет работать одинаково правильно!
Конечно при компиляции в FPC под Win32, это будет windows приложение, а в остальных случаях консольное DOS (16 битное) приложение.
Прокоментирую первые строчки программы (а дальше я думаю все понятно)

Эмуляция сопроцессора
Очень часто у многих возникает вопрос — почему при компиляции у меня возникает ошибка Error 116: Must be in 8087 mode to compile this.
Ответ: Вы используете один из следующих вещественных типов:
Single, Double, Extended, Comp
Для работы с этими типами, необходима эмуляция сопроцессора. Просто добавьте в начало программы директивы:

Оптимизация
Для уменьшения размера программ желательно добавить следующие директивы:
<$B->— быстрое вычисление логических условий;

Но с оптимизацией будьте очень осторожны !! Например, вот такой пример будет компилироваться, но выдаст неверный результат (попробуйте запустить программу без ключей <$B->и <$B+>. ):

var x, y: integer;

begin
x := 2;
<$B->
if (x > 5) and do_it then y := x
else y := 2 * x;

<$D->— отключить информацию для отладки ­(пропадает возможность отлаживать программу. (через F7)­). При этом размер сократится на 100-150 байт.

Описание других директив

<$N+>— сопроцессор
<$E+>— эмуляция сопроцессора
<$I->— отключение проверок ввода/вывода
<$R->— отключение проверок на границы массивов
<$S->— отмена проверки на переполнение стека
<$Q->— отмена проверок на границы типов ­(overflow, underflow)

Директивы компилятора (продолжение)

Директивы без параметров:

  • Выравнивание данных . (Глобальная директива). Эта директива позволяет переключаться между выравниванием переменных и типизированных констант по границе слова (состояние <А+>) и по границе байта (состояние ). При выравнивании на границу слова адресация происходит за 1 цикл обращения к памяти вместо двух.
  • Эмуляция математического сопроцессора . (Глобальная директива). Разрешает (<$E+>) или запрещает (<$E->) компоновку с библиотекой, которая будет эмулировать работу сопроцессора в случае его отсутствия. При компиляции в режиме <$N+, E+>Паскаль выполняет компоновку с полным эмулятором сопроцессора. Полученный EXE файл может выполняться на любой машине независимо от присутствия сопроцессора.
    В состоянии <$N+, E->компоновка осуществляется с гораздо меньшей по размеру библиотекой, которая может использоваться только при наличии сопроцессора. (Эта директива не производит никаких действий при использовании ее в модуле. Она действует только при компоновке программы)
  • Выбор модели вызова (Far/Near) . (Локальная директива). Управляет выбором типа вызова компилируемых процедур и функций.

    В процессоре поддерживается два типа вызовов и инструкций возврата управления — ближние (NEAR) и дальние (FAR). Ближние вызовы передают управление другой ячейке в пределах того же программного сегмента, а дальние вызовы позволяют перейти в другой программный сегмент.

Инструкция ближнего обращения CALL помещает в стек 16-битовый адрес возврата (только смещение), а инструкция дальнего вызова помещает в стек 32-битовый адрес возврата (адрес сегмента и смещение). Соответствующая инструкция RET извлекает из стека только смещение или адрес сегмента и смещение.

При компиляции в режиме <$F+>все процедуры и функции всегда используют дальнюю модель вызова (Far Call). При указании директивы <$F->компилятор Паскаля автоматически выбирает необходимый тип обращений: дальний, если процедура или функция описывается в интерфейсном разделе модуля (с расчетом на вызов из других блоков), и ближний в случае описания в разделе Implementation.
Существует несколько случаев, когда требуется использовать директиву <$F+>. Например, при использовании оверлейных модулей и при передаче процедур/функций в качестве параметров они должны быть откомпилированы с расчетом на дальнюю модель вызова.

  • Режим контроля ввода/вывода . (Локальная директива). Этот ключ задает (<$I+>) или отменяет (<$I->) генерацию кода, проверяющего результат выполнения операций ввода/вывода. Если операция ввода/вывода не может завершиться корректно и включен режим <$I+>, происходит ошибка времени выполнения (Run-time Error). При отключенном контроле (режим <$I->) аварийного останова программы не происходит, и результат операции может быть проанализирован с помощью функции IOResult.
    Внимание: после первого обращения к IOResult, флаг ошибки сбрасывается, и проанализировать ошибку во второй раз не получится. Чтобы это сделать, нужно сохранить значение IOResult в какой-либо переменной, и проверять именно значение этой переменной .
  • Использование сопроцессора . (Глобальная директива). При указании режима <$N->генерируется код для программного выполнения всех вещественных вычислений. При режиме <$N+>генерируется код для выполнения таких вычислений аппаратно с помощью сопроцессора.
  • Режим проверки границ . (Локальная директива). Приводит в действие (<$R+>) или отменяет (<$R->) генерирование кода проверки границ. При указании <$R+>все выражения со строками и массивы проверяются на нахождение индекса в допустимых пределах (все операторы присваивания переменным скалярных типов проверяются на принадлежность границам типа). При нарушении диапазона происходит ошибка времени исполнения. Эта директива увеличивает размер программы и уменьшает скорость ее исполнения. Поэтому желательно использовать режим <$R+>при отладке, выключая его в окончательной версии.
  • Режим проверки переполнения стека . (Локальная директива). Приводит в действие (<$S+>) или отменяет (<$S->) генерирование кода проверки границ. При указании <$S+>генерируется код, проверяющий, достаточно ли места в стеке выделено для локальных переменных. При недостатке места в стеке происходит ошибка времени исполнения. В режиме <$S->наиболее вероятно произойдет «зависание» системы.
  • Режим проверки параметров строкового типа . (Локальная директива). В состоянии <$V+>выполняется строгая проверка типа, прикоторой требуется, чтобы формальный и фактический параметр имели иденимчные строковые типы. В состоянии <$V->ф качастве фактического параметра допускается использовать любую переменную строкового типа, даже если ее описанная длинна не совпадает с длиной соответствующего формального параметра.
  • Директивы с параметрами:

    • Компоновка файла объектных кодов . Данная директива предписывает компилятору скомпоновать указанный файл с компилируемой программой или модулем. Директива <$L имя_файла>используется для компоновки кода, написанного на ассемблере.

    Кроме линковки с OBJ файлами данная директива очень часто используется для включения BGI драйверов и шрифтов в EXE файл (для полностью автономной работы программы). Для того чтобы произвести эту операцию нужно сделать следующее:

    1. С помощью утилиты BINOBJ.EXE (входящей в дистрибутив Турбо Паскаля) преобразовать BGI файл в OBJ-формат (работа с утилитой BINOBJ осуществляется из командной строки):

    2. Подключить полученный OBJ файл к программе:

    Сама инициализация графики (например, режима VGA) будет осуществляться так:

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

    Условная компиляция в Delphi, по сути, очень полезная и удобная возможность, которая позволяет Delphi компилировать или не компилировать код в зависимости от определенных символов условной компиляции. Говоря более простым языком (возможно и не совсем корректно), условная компиляция позволяет убрать или, наоборот, добавить часть кода исполняемого файла. Так или иначе, любой Delphi разработчик встречается с символами условной компиляции как минимум один раз за время работы над проектом.

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

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

    Теперь нажмите F9 и проверьте, что написано в отладчике в «Events»:

    Разберемся с тем, что мы только что написали.

    $IFDEF — это директива компилятора;

    DEBUG — условное определение или символ условной компиляции. Символ обязательно должен начинаться с буквы за которой может следовать любое количество букв, цифр и знаков подчеркивания, однако использоваться будут лишь первые 255 символов.

    Процедура отправляет строку в отладчик для отображения.

    Завершает условную компиляцию, инициированную последней директивой <$IFxxx>(почему не <$IFDEF>— смотрим далее).

    Таким образом, используя директивы и мы указали компилятору дословно следующее: если где-либо по ходу компиляции был встречено условное определение DEBUG, то надо выполнить OutputDebugString.

    Где определено условное определение DEBUG? Конкретно в этом случае, символ DEBUG можно найти, если зайти в настройки проекта: Project -> Options ->Delphi Compiler :

    Здесь же можно определить и свои собственные символы. Давайте, например, добавим свой символ условной компиляции TEST. Для этого открываем диалоговое окно редактирования символов условной компиляции (жмем кнопку «…» в строке «Conditional defines») и заносим наш символ в список:

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

    Теперь можете снова запустить приложения в режиме отладки и посмотреть, что в Events появится строка «TEST IS ON».

    Сейчас мы с вами рассмотрели самый простой способ использования условной компиляции в Delphi — использование директив и , а также определение собственного символа условной компиляции через свойства проекта. Вместе с этим, определить (включить) свой символ условной компиляции можно и в коде программы. Для этого используется директива компилятора . Воспользуемся этой директивой:

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

    и убедиться, что символ DEBUG выключен, а в окне Events не появится строка «debug is on».

    Двигаемся далее. Что делать, если нам необходимо вывести строку не когда символ включен, а именно тогда, когда он выключен? Здесь, опять же, есть варианты. Короткий вариант — воспользоваться директивой противоположной — она называется и код между и выполняется, если символ выключен:

    Второй вариант — использование директивы , если в зависимости от состояния символа условной компиляции вам надо выполнять различные участки кода:

    Соответственно, нет необходимости далее повторять этот же участок кода с использованием — работать будет, но прямо противоположно.

    Также следует обратить внимание на то, что все условные символы оцениваются в Delphi, когда вы выполняете Build проекта. Справка Delphi рекомендует для надежности пользоваться командой Project -> Build All Projects, чтобы быть уверенным, что все символы условной компиляции определены верно.

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

    Например, символ условной компиляции VER330 определен для Delphi 10.3 Rio и с его помощью можно определить какой код должен или не должен выполняться, в случае, если версия компилятора Delphi — 33. Например, воспользуемся фичей Delphi 10.3 Rio под названием Inline Variable Declaration:

    Сразу может возникнуть вопрос: как сделать так, чтобы приведенный выше код сработал не только в Delphi 10.3 Rio, но и в последующих версиях?
    Это можно сделать воспользовавшись, например, такой конструкцией:

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

    Здесь же стоит обратить внимание и на окончание блока — мы использовали директиву , как того требовала Delphi до версии Delphi XE4:

    • для директивы $IFDEF должна быть определена директива $ENDIF
    • для директивы $IF должна быть определена директива $IFEND

    В XE4 нам разрешили использовать для закрытия блоков <$IF>, и . Однако, если у вас возникают проблемы при использовании связки и , то вы можете использовать специальную директиву , чтобы потребовать использовать для именно <$IFEND>:

    Теперь, если в коде выше использовать директиву $ENDIF, то получим сообщение об ошибке:

    Директиву , кстати, можно использовать и с другими константами проекта, например, так:

    Так как наша константа Version содержит значение 2, то выполнится участок кода расположенный после . Можете сменить значение константы Version на 1, чтобы убедиться, что выполнится участок кода, где определена переменная s.

    Как я сказал выше, в Delphi есть целый набор предопределенных символов условной компиляции. Ознакомиться со всеми этими символами вы можете на этой странице справки по Delphi.

    1. Использование условной компиляции позволяет нам выполнять тот или иной код, в зависимости от того, какие константы и символы условной компиляции определены или не определены в проекте.
    2. Используя предопредленные символы условной компиляции можно указывать Delphi какой код необходимо выполнить, например, если программа собирается под Android, или, если поддерживается архитектура x64 и т.д.
    3. Директива $IF может использоваться с различными константами, в том числе и определенными самим разработчиком.

    При подготовке статьи использовалась следующая информация официальной справки по Delphi:

    $OverFlowChecks — Директива компилятора Delphi

    Профиль
    Группа: Экс. модератор
    Сообщений: 3103
    Регистрация: 28.3.2002
    Где: strawberry fields

    Репутация: 1
    Всего: 88

    Профиль
    Группа: Модератор
    Сообщений: 5658
    Регистрация: 2.7.2002
    Где: Санкт-Петербург

    Репутация: нет
    Всего: 118

    AntonSaburov
    Дата 18.12.2002, 21:30 (ссылка) | (нет голосов) Загрузка .

    Профиль
    Группа: Завсегдатай
    Сообщений: 3439
    Регистрация: 13.11.2002
    Где: в столице

    Репутация: 2
    Всего: 60

    директивы условной компиляции
    <$C+>и <$C->— директивы проверки утверждений
    <$I+>и <$I->— директивы контроля ввода/вывода
    <$M>и <$S>— директивы, определяющие размер стека

    <$M+>и <$M->— директивы информации времени выполнения о типах
    <$Q+>и <$Q->— директивы проверки переполнения целочисленных операций

    <$R>— директива связывания ресурсов

    <$R+>и <$R->— директивы проверки диапазона

    <$APPTYPE CONSOLE>— директива создания консольного приложения

    1) Директивы компилятора, разрешающие или запрещающие проверку утверждений.

    По умолчанию <$C+>или

    Область действия локальная

    Директивы компилятора $C разрешают или запрещают проверку утверждений. Они влияют на работу процедуры Assert,используемой при отладке программ. По умолчанию действует
    директива <$C+>и процедура Assert генерирует исключение EAssertionFailed, если проверяемое утверждение ложно.

    Так как эти проверки используются только в процессе отладки программы, то перед ее окончательной компиляцией следует указать директиву <$C->. При этом работа процедур Assert будет блокировано и генерация исключений EassertionFailed производиться не будет.

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

    2) Директивы компилятора, включающие и выключающие контроль файлового ввода-вывода.

    По умолчанию <$I+>или

    Область действия локальная

    Директивы компилятора $I включают или выключают автоматический контроль результата вызова процедур ввода-вывода Object Pascal. Если действует директива <$I+>, то при возвращении процедурой ввода-вывода ненулевого значения генерируется
    исключение EInOutError и в его свойство errorcode заносится код ошибки. Таким образом, при действующей директиве <$I+>операции ввода-вывода располагаются в блоке try. except, имеющем обработчик исключения EInOutError. Если такого блока нет, то обработка производится методом TApplication.HandleException.

    Если действует директива <$I->, то исключение не генерируется. В этом случае проверить, была ли ошибка, или ее не было, можно, обратившись к функции IOResult. Эта функция очищает ошибку и возвращает ее код, который затем можно анализировать. Типичное применение директивы <$I->и функции IOResult демонстрирует следующий пример:

    В этом примере на время открытия файла отключается проверка ошибок ввода вывода, затем она опять включается, переменной i присваивается значение, возвращаемое функцией IOResult и, если это значение не равно нулю (есть ошибка), то предпринимаются какие-то действия в зависимости от кода ошибки. Подобный стиль программирования был типичен до введения в Object Pascal механизма обработки исключений. Однако сейчас, по-видимому, подобный стиль устарел и применение директив $I потеряло былое значение.

    3) Директивы компилятора, определяющие размер стека

    Область действия глобальная

    Локальные переменные в процедурах и функциях размещаются в стеке приложения. При каждом вызове процедуры или функции ее локальные переменные помещаются в стек. При выходе из процедуры или функции эти локальные процедуры удаляются из стека.
    Директивы компилятора $M задают параметры стека приложения: его минимальный и максимальный размеры. Приложение всегда гарантированно имеет размер стека, равный его минимальной величине. Если при запуске приложения Windows обнаруживает, что не может
    выделить этот минимальный объем памяти, то выдается сообщение об этой ошибке.

    Если во время работы выясняется, что минимального размера стека не хватает, то размер увеличивается на 4 K, но не более, чем до установленного директивой максимального размера. Если увеличение размера стека невозможно из-за нехватки памяти или из-за достижения его максимальной величины, генерируется исключение EStackOverflow. Минимальный размер стека по умолчанию равен 16384 (16K). Этот размер может изменяться параметром minstacksize
    директивы <$M>или параметром number директивы <$MINSTACKSIZE>.

    Максимальный размер стека по умолчанию равен 1,048,576 (1M). Этот размер может изменяться параметром maxstacksize директивы <$M>или параметром number директивы <$MAXSTACKSIZE number>. Значение минимального размера стека может задаваться целым числом в диапазоне между1024 и 2147483647. Значение максимального размера стека должно быть не менее минимального размера и не более 2147483647. Директивы задания размера стека могут включаться только в программу и не должны использоваться в библиотеках и модулях.

    В Delphi 1 имеется процедура компилятора <$S>, осуществляющая переключение контроля переполнения стека. Теперь этот процесс полностью автоматизирован и директива <$S>оставлена только для обратной совместимости.

    4) Директивы компилятора, включающие и выключающие генерацию информации времени выполнения о типах (runtime type information — RTTI).

    По умолчанию <$M->или

    Область действия локальная

    Директивы компилятора $I включают или выключают генерацию информации времени выполнения о типах (runtime type information — RTTI). Если класс объявляется в состоянии <$M+>или является производным от класса объявленного в этом состоянии, то компилятор генерирует RTTI о его полях, методах и свойствах, объявленных в разделе published. В противном
    случае раздел published в классе не допускается. Класс TPersistent, являющийся предшественником большинства классов Delphi и все классов компонентов, объявлен в модуле Classes в состоянии <$M+>. Так что для всех классов, производных от него, заботиться о директиве <$M+>не приходится.

    5) Директивы компилятора, включающие и выключающие проверку переполнения при целочисленных операциях

    По умолчанию <$Q->или

    Область действия локальная

    Директивы компилятора $Q включают или выключают проверку переполнения при целочисленных операциях. Под переполнением понимается получение результата, который не может сохраняться в регистре компьютера. При включенной директиве <$Q+>проверяется переполнение при целочисленных операциях +, -, *, Abs, Sqr, Succ, Pred, Inc и Dec. После каждой из этих операций размещается код, осуществляющий соответствующую проверку. Если обнаружено переполнение,
    то генерируется исключение EIntOverflow. Если это исключение не может быть обработано, выполнение программы завершается.

    Директивы $Q проверяют только результат арифметических операций. Обычно они используются совместно с директивами <$R>, проверяющими диапазон значений при присваивании.
    Директива <$Q+>замедляет выполнение программы и увеличивает ее размер. Поэтому обычно она используется только во время отладки программы. Однако, надо отдавать себе отчет, что отключение этой директивы приведет к появлению ошибочных результатов расчета в случаях, если переполнение действительно произойдет во время выполнении программы. Причем сообщений о подобных ошибках не будет.

    6) Директива компилятора, связывающая с выполняемым модулем файлы ресурсов

    Область действия локальная

    Директива компилятора <$R>указывает файлы ресурсов (.DFM, .RES), которые должны быть включены в выполняемый модуль или в библиотеку. Указанный файл должен быть файлом ресурсов Windows. По умолчанию расширение файлов ресурсов — .RES. В процессе компоновки компилированной программы или библиотеки файлы, указанные в директивах <$R>, копируются в
    выполняемый модуль. Компоновщик Delphi ищет эти файлы сначала в том каталоге, в котором расположен модуль, содержащий директиву <$R>, а затем в каталогах, указанных при выполнении команды главного меню Project | Options на странице Directories/Conditionals диалогового окна в опции Search path или в опции /R командной строки DCC32.

    При генерации кода модуля, содержащего форму, Delphi автоматически включает в файл .pas директиву <$R *.DFM>, обеспечивающую компоновку файлов ресурсов форм. Эту директиву нельзя удалять из текста модуля, так как в противном случае загрузочный модуль не будет создан и сгенерируется исключение EResNotFound.

    7) Директивы компилятора, включающие и выключающие проверку диапазона целочисленных значений и индексов

    По умолчанию <$R->или

    Область действия локальная

    Директивы компилятора $R включают или выключают проверку диапазона целочисленных значений и индексов. Если включена директива <$R+>, то все индексы массивов и строк и все присваивания скалярным переменным и переменным с ограниченным диапазоном значений проверяются на соответствие значения допустимому диапазону. Если требования
    диапазона нарушены или присваиваемое значение слишком велико, генерируется исключение ERangeError. Если оно не может быть перехвачено, выполнение программы завершается.

    Проверка диапазона длинных строк типа Long strings не производится.
    Директива <$R+>замедляет работу приложения и увеличивает его размер. Поэтому она обычно используется только во время отладки.

    8) Директива компилятора, связывающая с выполняемым модулем файлы ресурсов

    Область действия локальная

    Директива компилятора <$R>указывает файлы ресурсов (.DFM, .RES), которые должны быть включены в выполняемый модуль или в библиотеку. Указанный файл должен быть файлом ресурсов Windows. По умолчанию расширение файлов ресурсов — .RES.
    В процессе компоновки компилированной программы или библиотеки файлы, указанные в директивах <$R>, копируются в выполняемый модуль. Компоновщик Delphi ищет эти файлы сначала в том каталоге, в котором расположен модуль, содержащий директиву <$R>, а затем в каталогах, указанных при выполнении команды главного меню Project | Options на странице Directories/Conditionals диалогового окна в опции Search path или в опции /R командной строки DCC32.

    При генерации кода модуля, содержащего форму, Delphi автоматически включает в файл .pas директиву <$R *.DFM>, обеспечивающую компоновку файлов ресурсов форм. Эту директиву нельзя удалять из текста модуля, так как в противном случае загрузочный модуль не будет создан и сгенерируется исключение EResNotFound.

    Cashey
    Дата 28.12.2002, 06:41 (ссылка) | (нет голосов) Загрузка .

    Профиль
    Группа: Экс. модератор
    Сообщений: 3103
    Регистрация: 28.3.2002
    Где: strawberry fields

    $OverFlowChecks — Директива компилятора Delphi

    Как сделать свои собственные сообщения при компилляции

    Как узнать версию компиллятора ?

    Какие есть директивы компилятора?

    <$I+>и <$I->— директивы контроля ввода/вывода
    <$M>и <$S>— директивы, определяющие размер стека

    <$M+>и <$M->— директивы информации времени выполнения о типах
    <$Q+>и <$Q->— директивы проверки переполнения целочисленных операций

    <$R>— директива связывания ресурсов

    <$R+>и <$R->— директивы проверки диапазона

    <$APPTYPE CONSOLE>— директива создания консольного приложения

    1) Директивы компилятора, разрешающие или запрещающие проверку утверждений.

    По умолчанию <$C+>или <$ASSERTIONS ON>
    Область действия локальная

    Директивы компилятора $C разрешают или запрещают проверку утверждений. Они влияют на работу процедуры Assert,используемой при отладке программ. По умолчанию действует
    директива <$C+>и процедура Assert генерирует исключение EAssertionFailed , если проверяемое утверждение ложно.
    Так как эти проверки используются только в процессе отладки программы, то перед ее окончательной компиляцией следует указать директиву <$C->. При этом работа процедур Assert будет блокировано и генерация исключений EassertionFailed производиться не будет.
    Директивы действуют на весь файл исходного кода независимо от того, в каком месте файла они расположены.

    2) Директивы компилятора, включающие и выключающие контроль файлового ввода-вывода.

    По умолчанию <$I+>или <$IOCHECKS ON>
    Область действия локальная

    Директивы компилятора $I включают или выключают автоматический контроль результата вызова процедур ввода-вывода Object Pascal . Если действует директива <$I+>, то при возвращении процедурой ввода-вывода ненулевого значения генерируется исключение EInOutError и в его свойство errorcode заносится код ошибки. Таким образом, при действующей директиве <$I+>операции ввода-вывода располагаются в блоке try . except , имеющем обработчик исключения EInOutError . Если такого блока нет, то обработка производится методом TApplication.HandleException .
    Если действует директива <$I->, то исключение не генерируется. В этом случае проверить, была ли ошибка, или ее не было, можно, обратившись к функции IOResult . Эта функция очищает ошибку и возвращает ее код, который затем можно анализировать. Типичное применение директивы <$I->и функции IOResult демонстрирует следующий пример:

    AssignFile ( F,s );
    Rewrite (F);

    <$I+>
    i:=IOResult ;
    if i <> 0 then
    case i of
    2 : .
    3 : .
    end ;

    В этом примере на время открытия файла отключается проверка ошибок ввода вывода, затем она опять включается, переменной i присваивается значение, возвращаемое функцией IOResult и, если это значение не равно нулю (есть ошибка), то предпринимаются какие-то действия в зависимости от кода ошибки. Подобный стиль программирования был типичен до введения в Object Pascal механизма обработки исключений. Однако сейчас, по-видимому, подобный стиль устарел и применение директив $I потеряло былое значение.

    3) Директивы компилятора, определяющие размер стека

    По умолчанию <$M 16384,1048576>
    Область действия глобальная

    Локальные переменные в процедурах и функциях размещаются в стеке приложения. При каждом вызове процедуры или функции ее локальные переменные помещаются в стек. При выходе из процедуры или функции эти локальные процедуры удаляются из стека.
    Директивы компилятора $M задают параметры стека приложения: его минимальный и максимальный размеры. Приложение всегда гарантировано имеет размер стека, равный его минимальной величине. Если при запуске приложения Windows обнаруживает, что не может выделить этот минимальный объем памяти, то выдается сообщение об этой ошибке.
    Если во время работы выясняется, что минимального размера стека не хватает, то размер увеличивается на 4 K, но не более, чем до установленного директивой максимального размера. Если увеличение размера стека невозможно из-за нехватки памяти или из-за достижения его максимальной величины, генерируется исключение EStackOverflow . Минимальный размер стека по умолчанию равен 16384 (16K). Этот размер может изменяться параметром minstacksize директивы <$M>или параметром number директивы <$MINSTACKSIZE>.
    Максимальный размер стека по умолчанию равен 1,048,576 (1M). Этот размер может изменяться параметром maxstacksize директивы <$M>или параметром number директивы <$MAXSTACKSIZE number >. Значение минимального размера стека может задаваться целым числом в диапазоне между1024 и 2147483647. Значение максимального размера стека должно быть не менее минимального размера и не более 2147483647. Директивы задания размера стека могут включаться только в программу и не должны использоваться в библиотеках и модулях.

    В Delphi 1 имеется процедура компилятора <$S>, осуществляющая переключение контроля переполнения стека. Теперь этот процесс полностью автоматизирован и директива <$S>оставлена только для обратной совместимости.

    4) Директивы компилятора, включающие и выключающие генерацию информации времени выполнения о типах ( runtime type information — RTTI).

    По умолчанию <$M->или <$ TYPEINFO OFF>
    Область действия локальная

    Директивы компилятора $M включают или выключают генерацию информации времени выполнения о типах ( runtime type information — RTTI). Если класс объявляется в состоянии <$M+>или является производным от класса объявленного в этом состоянии, то компилятор генерирует RTTI о его полях, методах и свойствах, объявленных в разделе published . В противном случае раздел published в классе не допускается. Класс TPersistent , являющийся предшественником большинства классов Delphi и все классов компонентов, объявлен в модуле Classes в состоянии <$M+>. Так что для всех классов, производных от него, заботиться о директиве <$M+>не приходится.

    5) Директивы компилятора, включающие и выключающие проверку переполнения при целочисленных операциях

    По умолчанию <$Q->или <$OVERFLOWCHECKS OFF>
    Область действия локальная

    Директивы компилятора $Q включают или выключают проверку переполнения при целочисленных операциях. Под переполнением понимается получение результата, который не может сохраняться в регистре компьютера. При включенной директиве <$Q+>проверяется переполнение при целочисленных операциях +, -, *, Abs , Sqr , Succ , Pred , Inc и Dec . После каждой из этих операций размещается код, осуществляющий соответствующую проверку. Если обнаружено переполнение, то генерируется исключение EIntOverflow . Если это исключение не может быть обработано, выполнение программы завершается.
    Директивы $Q проверяют только результат арифметических операций. Обычно они используются совместно с директивами <$R>, проверяющими диапазон значений при присваивании.
    Директива <$Q+>замедляет выполнение программы и увеличивает ее размер. Поэтому обычно она используется только во время отладки программы. Однако, надо отдавать себе отчет, что отключение этой директивы приведет к появлению ошибочных результатов расчета в случаях, если переполнение действительно произойдет во время выполнении программы. Причем сообщений о подобных ошибках не будет.

    6) Директивы компилятора, включающие и выключающие проверку диапазона целочисленных значений и индексов

    По умолчанию <$R>или <$RANGECHECKS OFF>
    Область действия локальная

    Директивы компилятора $R включают или выключают проверку диапазона целочисленных значений и индексов. Если включена директива <$R+>, то все индексы массивов и строк и все присваивания скалярным переменным и переменным с ограниченным диапазоном значений проверяются на соответствие значения допустимому диапазону. Если требования диапазона нарушены или присваиваемое значение слишком велико, генерируется исключение ERangeError . Если оно не может быть перехвачено, выполнение программы завершается.
    Проверка диапазона длинных строк типа Long strings не производится.
    Директива <$R+>замедляет работу приложения и увеличивает его размер. Поэтому она обычно используется только во время отладки.

    6) Директива компилятора, связывающая с выполняемым модулем файлы ресурсов

    Область действия локальная

    Директива компилятора <$R>указывает файлы ресурсов (.DFM, .RES), которые должны быть включены в выполняемый модуль или в библиотеку. Указанный файл должен быть файлом ресурсов Windows . По умолчанию расширение файлов ресурсов — .RES.
    В процессе компоновки компилированной программы или библиотеки файлы, указанные в директивах <$R>, копируются в выполняемый модуль. Компоновщик Delphi ищет эти файлы сначала в том каталоге, в котором расположен модуль, содержащий директиву <$R>, а затем в каталогах, указанных при выполнении команды главного меню Project | Options на странице Directories / Conditionals диалогового окна в опции Search path или в опции /R командной строки DCC32.
    При генерации кода модуля, содержащего форму, Delphi автоматически включает в файл . pas директиву <$R *.DFM>, обеспечивающую компоновку файлов ресурсов форм. Эту директиву нельзя удалять из текста модуля, так как в противном случае загрузочный модуль не будет создан и генерируется исключение EResNotFound .


    Все установленные в настройках опции компиляции можно вставить непосредственно в текст программы нажав клавиши Ctrl-O , O

    Как сделать свои собственные сообщения при компилляции ?

    destructor TumbSelectionTempTable.Destroy ;
    begin
    // Clear the temp tables.
    <$MESSAGE Warn ' - remember to free all allocated objects'>
    ClearAllOuterWorldFold ;
    if FSubjectsTempTableCreated then
    DropTempTable ( FTableName );

    FOuterWorldsFolded.Free ;
    FQuery.Free ;
    inherited ;
    end ;

    Работает только в Дельфи 6/7

    Как узнать версию компилятора?

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

    В Дельфи предопределены специальные константы компиляции для этого:

    Ver80 — Дельфи 1
    Ver90 — Дельфи 2
    Ver93 — С Buider 1
    Ver100 — Дельфи 3
    Ver110 — С Buider 3
    Ver120 — Дельфи 4
    Ver125 — С Buider 4
    Ver130 — Дельфи 5
    Ver140 — Дельфи 6
    Ver150 — Дельфи 7

    procedure TForm1.Button2Click( Sender : TObject );
    const Version=

    Изучаем отладчик, часть первая

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

    Так как большинство моих статей ориентированы на людей, интересующихся применением защиты в своем ПО, я решил, что подача материала с конкретными кусками кода защиты (наподобие опубликованных ранее) только запутает читателя. Гораздо проще начать от азов и потихоньку давать новый материал на уже готовой базе.

    Поэтому в данной статье будет рассмотрен один из базовых инструментов программиста – отладчик.
    Цели статьи: рассмотреть основные методы работы с отладчиком, показать его расширенные и редко используемые возможности, дать понимание работы механизмов отладчика на примерах и рассмотреть некоторый набор методов противодействия.

    Объем статьи получился неожиданно большим, поэтому я разбил ее на три части:

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

    Собственно, приступим.

    1.1. Применение точек остановки и модификация локальных переменных

    Одним из наиболее часто используемых инструментов встроенного отладчика является точка остановки (BreakPoint – далее BP). После установки BP, программа будет работать до тех пор, пока не достигнет точки остановки, после чего ее работа будет прервана и управление будет передано отладчику.

    Самым простым способом установки и снятия BP является горячая клавиша «F5» (или ее аналог в меню «Debug->Toggle breakpoint»). Есть и другие способы, но о них позже.

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

    Где ставить ВР – общего ответа конечно же нет. По сути ВР предназначена для облегчения изучения работы кода, в корректности работы которого мы не уверены, либо явно содержащего ошибку, которую мы с ходу не можем обнаружить.

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

    Давайте рассмотрим следующий пример.

    Есть задача: написать код, который 5 раз увеличит значение изначально обниленой переменной на единицу и еще один раз на число 123, после чего выведет результат в виде 10-тичного и 16-тиричного значения. Ожидаемые значения будут следующими: 128 и 00000080.

    Допустим, код будет написан с ошибкой:

    Данный код будет выводить какие угодно значения, но только не те, какие мы хотели, потому что мы не произвели инициализацию переменной «А» нулем. А так как переменная «А» локальна, значит, она расположена на стеке, и мы никогда не сможем предугадать, какое значение она примет в начале данной процедуры. Но будем считать, что уже конец рабочего дня, мы действительно устали (глаз замылился) и просто забыли написать строчку с инициализацией переменной.

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

    Должно получится примерно так, как на картинке. BP установлен на строчке Inc(A). Слева внизу можно наблюдать значение всех локальных переменных процедуры FormCreate (окно называется «Local Variables»), а именно, переменной Self (она передается неявно и всегда присутствует в методах класса), параметра Sender, и непосредственно локальной переменной «А». Ее значение 19079581. Слева в центре в «WatchList» значение переменной «B».

    Даже бегло взглянув на значения обеих переменных и выполненные три строчки кода, мы сможем понять, что значение переменной «А» не соответствует ожидаемому. Так как должно было выполнится два инкремента на единицу и еще одно увеличение на число 123, мы должны были увидеть значением переменной «А» число 125, а раз там другое значение, то это может означать только одно – изначальное значение переменной «А» было не верным.

    Для проверки правильности нашего предположения, давайте изменим текущее значение переменной «A» на верное и продолжим выполнение программы, чтобы проверить – те ли результаты вернет процедура, которые ожидались.

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

    Первый – «Evaluate/Modify», вызывается либо через меню, либо по горячей клавише «Ctrl+F7». Это очень простой инструмент с минимумом функционала, он наиболее часто используется.

    Для изменения в нем значения переменной, достаточно указать новое значение в поле «New value» и нажать клавишу «Enter» или кнопку «Modify».
    Второй инструмент – «Inspect», доступен так же либо через меню «Run», либо уже непосредственно из диалога «Evaluate/Modify». Это более продвинутый редактор параметров, о нем чуть позже.

    После изменения значения переменной «А», обратите внимание на изменения в списке значений локальных переменных:

    Переменная «А» приняла правильное значение, и теперь мы можем продолжить выполнение нашего приложения нажатием «F9» или через меню, выбрав пункт «Run». В результате такого вмешательства с помощью отладчика, процедура выдаст нам ожидаемые числа 128 и 00000080, и мы уже можем смело исправлять код процедуры, т. к. мы нашли в нём причину ошибки и проверили его исполнение с правильно заданным значением переменной «A».

    Теперь вернемся к «Inspect». Помимо двух указанных способов его вызова, он так же вызывается двойным кликом на переменной в окне «Local Variables», либо через контекстное меню при правом клике на ней, либо по горячей клавише «Alt+F5».

    Это более «продвинутый» редактор свойств переменных, но его использование оправдано при изменении свойств объектов. Для изменения обычной переменной он несколько не удобен, и вот почему.

    При его вызове сначала вы увидите вот такой диалог:


    В нем будет указано описание переменной, ее расположение в памяти и ее текущее значение, а для изменения нужно будет еще раз нажать на кнопку с троеточием, после чего появится дополнительное окно:

    Лишние телодвижения для такой простой операции, как изменения значения обычной переменной, явно избыточны. Но если его использовать для изменения свойств объектов, то картинка немного поменяется.

    Через «Evaluate/Modify» доступ к свойствам объекта несколько затруднен тем, что он не предоставляет информации непосредственно об исследуемом объекте. Например, для получения хэндла канваса формы, нам придется в нем набрать следующий текст: «(Sender as TForm1).Canvas.Handle» – что несколько не удобно, ведь мы можем и опечататься, да и просто банально забыть название того или иного свойства.

    В случае с «Inspect» такой проблемы не будет.

    К примеру, давайте откроем диалог «Inspect» не для переменной «А», а для переменной Self (которая, как я и говорил ранее, всегда неявно присутствует для всех методов объектов).

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

    1.2. Трассировка (пошаговая отладка)

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

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

    А умеет он следующее:

    1. Команда «Trace Into» («F7») – отладчик выполнит код текущей строчки кода и остановится на следующей. Если текущая строчка кода вызывает какую либо процедуру, то следующей строчкой будет первая строка вызываемой процедуры.
    2. Команда «Step Over» («F8») – аналогично первой команде, но вход в тело вызываемой процедуры не происходит.
    3. Команда «Trace to Next Source Line» («Shift+F7») – так же практически полный аналог первой команды, но используется в окне «CPU-View» (данный режим отладки не рассматривается в статье).
    4. Команда «Run to Cursor» («F4») – отладчик будет выполнять код программы до той строчки, на которой сейчас находится курсор (с условием, что в процессе выполнения не встретилось других ВР).
    5. Команда «Run Until Return» («Shift+F8») – отладчик будет выполнять код текущей процедуры до тех пор, пока не произойдет выход из нее. (Часто используется в качестве контрприема на случайно нажатую «F7» и так же с условием, что в процессе выполнения не встретилось других ВР).
    6. В старших версиях Delphi доступна команда «Set Next Statement», при помощи которой мы можем изменить ход выполнения программы, установив в качестве текущей любую строку кода. Так же эта возможность доступна в редакторе кода там, где можно перетащить стрелочку, указывающую на текущую активную строчку в новую позицию.

    Подробного рассмотрения данные команды не требуют. Остановимся только на команда «Trace Into» («F7»).

    Для примера возьмем такой код:

    При выполнении трассировки, в тот момент, когда мы находимся на строчке S.Add(), у нас могут быть два варианта реакции отладчика:

    1. мы войдём внутрь метода TStringList.Add,
    2. мы туда не войдём.

    Обусловлено данное поведение настройками вашего компилятора. Дело в том что в составе Delphi поставляется два набора DCU для системных модулей. Один с отладочной информацией, второй — без. Если у нас подключен второй модуль, то команда «Trace Into» («F7») в данном случае отработает как «Step Over» («F8»). Настраивается переключение между модулями в настройках компилятора:

    И отвечает за данный функционал параметр «Use Debug DCUs».

    1.3. Подробнее о настройках компилятора

    Опции в закладке с настройками компилятора влияют непосредственно на то, какой код будет генерироваться при сборке вашего проекта. Очень важно не забывать, что при изменении любого из пунктов данной вкладки, требуется полная пересборка проекта («Project > Build») для того, чтобы изменения вступили в силу. Данные настройки непосредственно влияют на поведение вашего кода в различных ситуациях, а так же на состав информации, доступной вам при отладке проекта.

    Рассмотрим их поподробнее:

    Группа «Code generation»

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

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

    Как видите, значения ранее доступных переменных Self и Sender, более не доступны. Так же из-за отключенного параметра «Use Debug DCUs» произошло кардинальное изменение в окне «Call Stack», ранее заполненного более подробной информацией о списке вызовов.
    Более того, инструмент «Inspect» так же отказывается работать с объектом Self, выдавая следующую ошибку:

    Параметры «Stack Frames» и «Pentiom-safe FDIV»

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

    Параметр «Record field alignment»

    Глобальная настройка выравнивания неупакованных записей, которая может быть изменена локально в пределах модуля директивой «<$Align x>» или «<$A x>»

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

    Размер данной записи, который мы можем получить через SizeOf(T), будет для каждой из настроек выравнивания свой:

    Группа «Syntax options»

    Тут лучше вообще ничего не трогать. Ибо, если постараться, то можно сделать даже так, что стандартная VCL откажется собираться.

    Единственно остановлюсь на параметре «Complete boolen eval», ибо периодически некоторые его включают. Он грозит ошибкой при выполнении следующего кода:

    Так как, при включении данной настройки, булево выражение будет проверяться целиком, то произойдет ошибка при обращении к Value.Count, не смотря на то, что первая проверка определила, что параметр Value обнилен. А если вы включите (например) параметр «Extended syntax», то данный код у вас вообще не соберется, пожаловавшись на необъявленную переменную Result.

    Группа «Runtime errors»

    Параметр «Range checking»

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

    В самом простом случае вам будет сгенерировано исключение при выполнении вот такого кода:

    Здесь мы просто пытаемся обратится к элементу массива, и в принципе, при отключенной опции «Range checking», если мы не выйдем за границу выделенной памяти, данный код нам грозит только тем, что в заголовке формы появится некая непонятная строка.

    Что неприятно, но некритично для выполнения программы. Гораздо хуже, если вы ошиблись с границами блока при попытке записи в него – в этом случае может произойти разрушение памяти приложения.

    Рассмотрим такой пример, оптимизацию отключим:

    Как вы думаете, чему будет равно значение числа HazardVariable после выполнения данного кода? Нет, не 100. Оно будет равно 4. Так как мы ошиблись при выборе типа итератора и вместо TMyEnum2 написали TMyEnum1, произошел выход за диапазон границ массива и затерлись данные на стеке, изменив значения локальных переменных хранящихся на нём же.

    С включенной оптимизацией ситуация будет еще хуже. Мы получим следующую ошибку:

    По данному описанию мы даже не сможем предположить, где именно произошло исключение, и почему оно произошло, так как адреса, упомянутые в тексте ошибки, не принадлежат памяти приложения, и если, не дай бог, такая ошибка возникнет у клиента – исправить ее по данному описанию мы не сможем.

    Поэтому возьмите себе за правило – отладка приложения всегда должна происходить с включенной настройкой «Range checking»!

    Так же данный параметр контролирует выход за границы допустимых значений при изменении значения переменных. Например, будет поднято исключение при попытке присвоения отрицательного значения беззнаковым типам наподобие Cardinal/DWORD, или при попытке присвоить значение большее, чем может содержать переменная данного типа, например, при присвоении 500 переменной типа Byte и т. п…

    Параметр «I/O cheking»

    Отвечает за проверку результатов ввода/вывода при работе с файлами в стиле Pascal.

    Не уверен, что еще остался софт, использующий данный подход, но если вы вдруг все еще работаете с Append/Assign/Rewrite и т. п., то включайте данный параметр при отладке приложения.

    Параметр «Overflow cheking»

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

    Чтобы было проще понять различия между данным параметром и «Range checking», рассмотрим следующий код:

    Данный код не поднимет исключения при включенном параметре «Overflow cheking». Хоть здесь и присваиваются переменным не допустимые значения, но не производится математических операций над ними. Однако исключение будет поднято при включенном параметре «Range checking».

    А теперь рассмотрим второй вариант кода:

    Здесь уже не будет реакции от параметра «Range checking», но произойдет поднятие исключения EIntegerOverflow, за который отвечает «Overflow cheking», на строчках Inc(B) и C := C — 1 из-за того, что результат арифметической операции не может быть сохранен в соответствующей переменной.
    Таким образом, при работе с переменными оба параметра взаимодополняют друг друга.

    «Overflow cheking» не настолько критичен, как «Range checking», но всё же желательно держать его включенным при отладке приложения.

    Небольшой нюанс: если вы вдруг реализуете криптографические алгоритмы, то в них, как правило, операция переполнения является штатной. В таких ситуациях выносите код в отдельный модуль и в начале модуля прописывайте директиву «<$OVERFLOWCHECKS OFF>» для отключения проверки переполнений в текущем модуле.

    С данной вкладкой все очень просто. Все параметры, за исключением параметра «Assertions», никоим образом не влияют на финальный код вашего приложения. В зависимости от активности тех или иных параметров изменяется полнота отладочной информации в DCU файле для каждого модуля. На основе данной информации отладчик производит синхронизацию ассемблерного листинга программы с ее реальным кодом, реализованным программистом, распознает локальные переменные и т. п. При компиляции приложения данная отладочная информация не помещается в теле приложения.
    Единственным исключением является параметр «Assertions» – он отвечает за работу процедуры Assert(). Если данный параметр отключен – Assert не выполняется, в противном случае – выполняется, причем его код так же будет помещен в тело приложения на этапе компиляции.

    На этапе отладки приложения желательно держать все параметры из групп «Runtime errors» и «Debugging» включенными, и отключать их при финальной компиляции релизного приложения. В Delphi 7 и ниже это придется делать руками, но, начиная с Delphi 2005 и выше, появилась нормальная поддержка билдов проекта, в которой можно указывать данные комбинации флагов персонально для каждого типа сборки.

    1.4. Окно стека вызовов («Call Stack»)

    Если ВР является нашим основным инструментом при отладке приложения, то «Call Stack» второй по значимости.

    Выглядит данное окно следующим образом:

    Он содержит полное описание вызовов, которые были выполнены до того, как отладчик прервал выполнение программы на установленном ВР (или остановился из-за возникновения ошибки). Например, на скриншоте изображен стек вызовов, произошедших при нажатии кнопки на форме. Начался он с прихода сообщения WM_COMMAND (273) в процедуру TWinControl.DefaultHandler.

    Имея на руках данный список, мы можем быстро переключаться между вызовами двойным кликом (или через меню «View Source»), просматривать список локальных переменных для каждого вызова («View Locals»), устанавливать ВР на любом вызове.

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

    Например вот так будет выглядеть стек вызовов при возникновении ошибки EAbstractError:

    В данном случае достаточно найти самый первый сверху вызов, код которого расположен не в системных модулях Delphi, чтобы с большой долей вероятности сказать, что ошибка именно в нём. Таким вызовом является Unit1.TForm1.Button1Click() — это обработчик кнопки Button1 в котором выполнялся следующий код:

    Еще одним из вариантов использования является отслеживание вызовов тех или иных функций. Например, у нас очень большой код приложения, и где-то глубоко внутри него происходит вызов MessageBox, но мы не можем с наскоку найти это место, чтобы локализовать место вызова именно этого MessageBox. Для этого можно воспользоваться следующим методом:

    1. перейти в модуль, где объявлен вызов интересующей нас функции (в данном случае это windows.pas),
    2. найти её объявление (строка с синей точкой function MessageBox; external user32. ),
    3. установить на данной строке ВР и запустить программу.

    Как только из любого места программы произойдет вызов MessageBox, сработает наш ВР и мы сможем – на основании данных «Call Stack» – выяснить точное место его вызова.

    1.5. Работа с расширенными свойствами точек остановки

    Допустим, мы точно знаем в каком месте алгоритма у нас происходит ошибка, но ошибка эта происходит не всегда, а при выполнении алгоритмом операций только с определенными данными, в этом случае нет смысла просматривать все сработки ВР в ожидании того момента, когда алгоритм начал работать не верно, гораздо проще указать нашей ВР ожидать необходимые данные, пропуская все лишнее.

    Делается это посредством диалога настроек свойств точки остановки. Вызывается он либо через свойства BP в коде приложения.

    Либо в окне «Breakpoint list» так же через свойства выбранной BP.

    Выглядит диалог настроек следующим образом:

    Параметр «Condition» отвечает за условие срабатывания точки остановки.
    Параметр «Pass count» указывает, сколько таких условий нужно пропустить, прежде чем ВР будет активирована, причем подсчёт количества срабатываний ведется от самого первого, с учетом значения параметра «Condition».

    Рассмотрим абстрактный пример:

    Допустим, ВР установлена на седьмой строчке (RandomValue := . ). Если программу просто запустить, то мы получим на руки ровно 100 срабатываний ВР. Для того чтобы данная ВР срабатывала каждый десятый вызов необходимо в свойстве «Pass count» выставить значение 10. В этом случае мы получим ровно десять срабатываний ВР, в тот момент кода итератор «I» будет кратен десяти.

    Допустим, теперь мы хотим начать анализ после 75 итерации включительно, для этого выставим следующее условие в параметре «Condition»: I > 75. В этом случае данная ВР сработает всего два раза: в тот момент, когда итератор «I» будет равен 85, и второй раз, при значении 95.

    Произошло это по следующим причинам:

    В первом случае, когда у нас не было условия, ВР срабатывала на каждой итерации цикла, но т. к. был указан параметр «Pass count», управление не переходило к отладчику, а происходило всего лишь увеличение количество срабатываний ВР до тех пор, пока их количество не становилось равным заданному в «Pass count». Поэтому мы получали управление только каждую десятую итерацию (после чего счетчик обнулялся).

    Во втором случае увеличение счетчика сработок начало происходить только после выполнения изначального условия «Condition», т. е., пока итератор «I» был меньше или равен числу 75, отладчик считал, что условие не выполнено и продолжал выполнение программы. Как только первое условие выполнилось, началось увеличение количества срабатываний, которое стало равным значению параметра «Pass count» именно в тот момент, когда итератор «I» достиг значения 85.

    Естественно, если мы хотим, чтобы ВР начала срабатывать сразу после превышения итератором «I» числа 75, то параметр «Pass count» необходимо выставить в ноль.

    Группируя эти два параметра мы можем более гибко настроить условия срабатывания наших ВР.

    Теперь рассмотрим один небольшой нюанс.

    Условия передачи управления отладчику, указанные в свойствах ВР, рассчитываются самим отладчиком. Т.е. грубо (взяв за основу вышеописанный пример), не смотря на то, что мы прервались на ВР всего лишь два раза, на самом деле прерывание произошло все 100 раз, просто мы не получали управление в те моменты, которые не соответствовали заданным нами условиям.

    Чем это плохо: если подобным образом анализировать достаточно долгий по продолжительности цикл (например, от десяти тысяч итераций и выше), то отладка программы может очень сильно затормозить вашу работу.

    Можно проверить на следующем коде:

    Установим ВР на той же седьмой строчке и укажем в параметре «Condition» значение I=9999. Даже на таком маленьком цикле нам придётся ждать срабатывания условия в районе 3-5 секунд. Конечно же, это не удобно. В таких случаях проще модифицировать код следующим образом:

    … и поставить ВР на Beep, чем ждать столь продолжительное время. В этом случае мы получим управление на практически мгновенно.
    (В релизной сборке проекта директива DEBUG будет отсутствовать и отладочный код не попадет в неё, но лучше, после отладки, все же не забывать удалять все эти отладочные Beep-ы. )

    Подобные «тормоза» обусловлены тем, что всё взаимодействия отладчика с отлаживаемым приложением происходит через механизм структурной обработки исключений (SEH), более известный Delphi программистам через куцую обертку над ним в виде try..finally..except. Работа с SEH является одной из наиболее «тяжелых» операций для приложения. Дабы не быть голословным и показать его влияние на работу программы наглядно, рассмотрим такой код:

    В функции Test1 и Test2 происходит инкремент переданного значения 100 миллионов раз.

    В первом случае она выполняется в районе 210 миллисекунд, тогда как во втором случае – в четыре с небольшим раза дольше, а изменений между ними по сути нет – всего лишь глухой try..finally.

    Это, кстати тоже вам в «копилочку» – по возможности не вставляйте обработку исключений внутрь циклов, лучше выносите её за пределы…

    Не рассмотренным у нас остался параметр «Group», он отвечает за включение BP в определенную группу точек остановки. Группа – понятие условное, на самом деле это некий идентификатор, произвольно задаваемый разработчиком, но удобен он тем, что к данным идентификаторам можно применить групповые операции управляя активностью всех BP, входящих в данную группу.

    Групповые операции настраиваются в расширенных настройках ВР:

    Отвечают за это параметры: «Enable group» – активирующий все ВР группы, и «Disable group» – отключающий все ВР входящие в группу.

    Так же при групповых операциях часто применяется параметр «Break», который отвечает за действия отладчика при достижении ВР. Если данный параметр не активен, то прерывания выполнения программы при достижении данной ВР не происходит.
    Важно – данный параметр не отключает саму ВР.

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

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

    Перед компиляцией примера обязательно включите в настройках компилятора опцию «Overflow cheking» и отключите оптимизацию.

    После запуска данного кода, произойдет исключение на шестнадцатой строчке

    Обычно, для того чтобы разобраться в причинах ошибки, ставят ВР на строку, в которой произошло поднятие исключения, но в данном конкретном примере нам это не поможет. Причина проста: данная строчка десятки раз подряд будет выполнена успешно, и, если мы поставим ВР на ней, то все эти десятки раз нам придется нажимать «F9» до тех пор, пока мы не достигнем непосредственно самого исключения.

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

    Сделаем это следующим образом:

    1. Поставим ВР на шестнадцатой строчке и назначим ей группу «level2BP».
    2. Отключим данную группу, чтобы установленная ВР не срабатывала раньше времени. Для этого в процедуре FormCreate поставим новую ВР на ShowMessage и в параметре «Disable group» укажем группу «level2BP». Чтобы не прерываться на новой ВР, в его настройках отключим параметр «Break».
    3. В функции Level1 устанавливаем ВР на строчке №25. Посчитаем, сколько раз выполнится данная ВР перед появлением ошибки.
    4. Выясняем, что было 9 прерываний (итератор I в этот момент равен восьми). Значит, нам нужно пропустить первые 8 прерываний, в которых ошибок не обнаружено, и на девятом включить ВР из группы «level2BP». Для этого заходим в свойства текущей ВР и выставляем в параметре «Condition» значение I=8, после чего исключаем его из обработки через отключение параметра «Break» и в настройках «Enable group» прописываем «level2BP».
    5. Перезапустив приложение, мы сразу прервемся в процедуре Level2, но не в момент самой ошибки – ошибка произойдет через несколько итераций. Несколько раз нажмем F9, считая количество итераций, и выясним, что это происходит в тот момент, когда итератор I был равен 5. В параметре «Condition» текущей ВР установим условие I=5, после чего можно смело перезапускать приложение.
    6. Первое же прерывание в отладчике произойдет непосредственно в месте возникновения ошибки, откуда и можно приступать к разбору причин ее возникновения.

    Если из описания примера не все понятно — посмотрите ролик, демонстрирующий всю последовательность действий:
    rouse.drkb.ru/blog/bp3.mp4 (17 Мб).
    (я извиняюсь, но вставить ссылку так, чтобы ролик отображался прямо в теле статьи, у меня не получилось, поэтому только ссылка)

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

    Почему в примере не использовался параметр «Pass Count», а условия задавались через параметр «Condition»? Дело в том, что «Pass Count» просто отключает прерывание на ВР. Сама же ВР выполняется (т. к. условия её выполнения описаны в параметре «Condition») и раз она выполнилась, то выполняются и её групповые операции.

    Осталось рассмотреть еще несколько параметров.

    Параметр «Ignore subsequent exceptions» отключает реакцию отладчика на любое исключение, возникшее после выполнения ВР с включенным данным параметром.

    Параметр «Handle subsequent exceptions» отменяет действие предыдущего параметра, возвращая отладчик в нормальный режим работы.

    Чтобы посмотреть как это выглядит, создадим такой код:

    На первом ShowMessage поставьте ВР, отключите его, сняв галку с параметра «Break», и включите параметр «Ignore subsequent exceptions».

    На втором ShowMessage сделайте то же самое, только включите параметр «Handle subsequent exceptions».

    Запустите приложение из отладчика и пощелкайте по кнопкам в следующем порядке:

    1. Button1
    2. Button2
    3. Button3
    4. Button4
    5. Button2
    6. Button3

    Не смотря на то, что кнопки Button2 и Button3 генерируют исключение, на этапе 2 и 3 отладчик на них никак не прореагирует, мы дождемся от него реакции только на этапах 5 и 6 после того, как активируем нормальную обработку исключений нажатием кнопки Button4.

    Осталось 2 параметра:

    «Log message» – любая текстовая строчка, которая будет выводится в лог событий при достижении ВР.

    «Eval expression» – при достижении ВР, отладчик вычисляет значение данного параметра и (в случае если включен флаг «Log result») выводит его в лог событий. Значение для вычисления может быть любым, хоть тот же «123 * 2».

    1.6. Использование «Data breakpoint», «Watch List» и «Call Stack»

    Все, что мы рассматривали ранее, относилось к так называемым «Source Breakpoint». Т. е. к точкам остановки, устанавливаемым непосредственно в коде приложения.

    Но, помимо кода, мы работаем с данными (переменными, массивами, просто с участками выделенной памяти) и у отладчика есть возможность устанавливать BP на адреса, по которым эти данные расположены в памяти, при помощи «Data breakpoint».

    Установка ВР на адрес памяти производится через «Watch List» (не во всех версиях Delphi) или в окне «Breakpoint List» при помощи «Add Breakpoint->Data Breakpoint», где, в появившемся диалоге, указываем требуемый адрес, размер контролируемой области или имя переменной. В случае указания имени переменной, отладчик попробует вычислить ее расположение в памяти и (если это возможно) установит ВР.

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

    Что такое область видимости переменной – вы должны знать. Глобальные переменные доступны нам всегда, и, даже без запуска приложения, отладчик предоставляет нам возможность устанавливать «Data breakpoint» на изменения в таких переменных. Правда, в данном случае он рассчитывает адрес такой переменной на основании предыдущей сборки приложения, и не факт, что он совпадет с ее расположением при следующем запуске. Ситуация гораздо хуже с локальными переменными. Область видимости переменной – это не просто так введенное понятие, локальные переменные расположены на стеке, и, как только они выходят из области видимости, место, занимаемое ими ранее, используется под хранение совершенно других данных. Таким образом установить «Data breakpoint» на локальную переменную можно только в тот момент, пока она не вышла из области видимости.

    Те, кто ранее работал с профессиональными отладчиками, вероятно узнают в «Data breakpoint» один из базовых инструментов анализа приложения – «Memory Breakpoint».

    К сожалению, отладчик Delphi не позиционируется как профессиональное средство отладки сторонних приложений, поэтому столь полезный инструмент как «Memory Breakpoint» представлен в нем в обрезанном варианте, где от него оставлена только возможность контроля адреса памяти на запись.

    Но даже в этаком ограниченном варианте он остается отличным инструментом для контроля изменений в памяти приложения, особенно для отслеживания ошибок при адресной арифметике.

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

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

    Нажав на «Break», мы окажемся где-то внутри модуля «system»:

    Код, на котором мы прервались, ничего нам не может сказать о причине возникновения ошибки, но у нас есть окно «Call Stack», на основании которого мы можем сделать вывод, что ошибка произошла при вызове процедуры ShowCaption в главном модуле программы.
    Если установить BP в данной процедуре и перезапустить программу, а затем, при срабатывании ВР, проверить значение переменной Caption, то окажется что данная переменная не доступна:

    Это означает, что где-то произошло разрушение памяти и затерлись данные по адресу переменной Caption. Определить это место нам поможет «Data breakpoint».

    1. Дождемся инициализации переменной Caption, для этого установим ВР на строчке FT.Description := ‘Test Description’;.
    2. При срабатывании ВР, добавим переменную FP.Caption в «Watch List» и в свойствах этой переменной выберем «Break When Changed». Если данного пункта меню у вас нет (например, в Delphi 2010 он отсутствует), то установим «Data breakpoint» немного другим способом. В «Breakpoint List» выбираем «Add->Data Breakpoint», в появившемся диалоге указываем имя переменной FP.Caption и нажимаем ОК.
    3. Запускаем программу на выполнение.

    После выполнения этих действий, программа остановится на строчке №68 – Inc(Value). Особенность «Data breakpoint» в том, что остановка происходит сразу после произошедших изменений, а не при попытке изменения контролируемой памяти, поэтому место, где происходит запись по адресу переменной FP.Caption, находится строчкой выше – это строка Value^ := ValueData[I].

    Теперь, найдя проблемное место, мы можем исправить и саму ошибку. Она заключается в том, что длина строки ValueData, которую мы пишем в буфер Data, превышает размер буфера, из-за чего происходит перезапись памяти, в которой расположены переменные Caption и Description.

    1.7. В заключение

    На этом я заканчиваю краткий обзор возможностей интегрированного отладчика. Осталось несколько нерассмотренных нюансов, как то: настройка игнорируемых исключений, ВР при загрузке модуля и т.п., но они несущественны и крайне редко применяются на практике.
    Так же нерассмотренным остался режим отладки в окне «CPU-View» и связанные с ним Address Breakpoint. Его я так же решил пропустить, т.к. читателям не знакомым с ассемблером мое объяснение не даст ничего, а более подкованные специалисты и без меня знают что такое CPU-View и как его правильно применять :)

    Во второй части статьи, будет рассмотрена программная реализация отладчика. В ней будет показано, что именно происходит при установке BreakPoint, показана обратная сторона Data Breakpoint, не реализованная в отладчике Delphi, показано как в действительности производится трассировка (двумя методами, классический через TF флаг и на основе GUARD страниц), а так же рассмотрен механизм Hardware Breakpoint, тоже отсутствующий в интегрированном отладчике Delphi.

    Отдельная благодарность сообществу форума «Мастера Дельфи» за помощь при подготовке статьи, а также персональное спасибо Андрею Васильеву aka Inovet, Тимуру Вильданову aka Palladin и Дмитрию aka Брат Птибурдукова за вычитку материала и ценные советы.

    Александр (Rouse_) Багель
    Москва, октябрь 2012

    Delphi: How do i use $OVERFLOWCHECKS OFF to disable overflow checks?

    i have bit of code that causes an underflow:

    The subtraction itself does generate an overflow (underflow), but i don’t want Delphi to throw an EIntOverflow exception. So i try disabling the generation of overflow checking code by disabling overflow checking:

    Yet even with the OVERFLOWCHECKS OFF option, it still throws an exception. And the generated code still contains the check:

    A reminder of the documentation on $Q :

    Type Switch
    Syntax <$Q+>or <$Q->
    <$OVERFLOWCHECKS ON>or <$OVERFLOWCHECKS OFF>
    Default <$Q->
    <$OVERFLOWCHECKS OFF>
    Scope Local

    Remarks

    The $Q directive controls the generation of overflow checking code. In the <$Q+>state, certain integer arithmetic operations (+, -, *, Abs, Sqr, Succ, Pred, Inc, and Dec) are checked for overflow. The code for each of these integer arithmetic operations is followed by additional code that verifies that the result is within the supported range. If an overflow check fails, an EIntOverflow exception is raised (or the program is terminated if exception handling is not enabled).

    The $Q switch is usually used in conjunction with the $R switch, which enables and disables the generation of range-checking code. Enabling overflow checking slows down your program and makes it somewhat larger, so use <$Q+>only for debugging.

    How do i use $OVERFLOWCHECKS OFF to disable the generation of overflow checking code?

    Mason’s answer worked. The revised code is:

    For google crawler, alternate question phrasing: How to temporarily disable overflow checking in Delphi?

    Директивы компилятора для Debug/Release

    Vex
    Дата 28.12.2002, 07:08 (ссылка) | (нет голосов) Загрузка .
    14.11.2020, 20:14

    Директивы компилятора
    Ребят знает кто как отключить ошибки такого рода(см.ниже) Она выскакивает если оставить.

    Различия между Debug и Release
    Чем отличаются Debug и Release версии файла, кроме размера самого файла на выходе? Я так понял они.

    В чем отличие Debug от Release
    Народ, конкретно чем отличается дебаг от релиза. Я новичок совсем, поэтому не шарю. Но компилируя в.

    Почему Release и Debug работают по разному
    Уже не в первый раз происходит так, что откомпилированный в режиме Release проект работает не так.

    Почему некоторые методы в Release не работают, тогда как в Debug все нормально?
    Всем привет! Пришло время узнать, а собственно почему некоторые методы в Reliase не работают, так.

    Директивы по версиям компилятора Delphi: <$ IFDEF VER180>— 2020

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

    Если вы планируете писать код Delphi, который должен работать с несколькими версиями компилятора Delphi, вам необходимо знать, в каких версиях компилируется ваш код.

    Предположим, вы пишете свой собственный (коммерческий) пользовательский компонент. Пользователи вашего компонента могут иметь разные версии Delphi, чем у вас.

    Если они попытаются перекомпилировать код компонента (ваш код) — у них могут быть проблемы! Что если вы использовали параметры по умолчанию в ваших функциях, а у пользователя Delphi 3?

    Директива компилятора: $ IfDef

    Директивы компилятора — это специальные синтаксические комментарии, которые мы можем использовать для управления функциями компилятора Delphi. Компилятор Delphi имеет три типа директив: переключение директив, директивы параметров а также условные директивы, Условная компиляция позволяет нам выборочно компилировать части исходного кода в зависимости от того, какие условия установлены.

    Директива компилятора $ IfDef запускает секцию условной компиляции.

    Синтаксис выглядит так:

    DefName представляет так называемый условный символ. Delphi определяет несколько стандартных условных символов. В приведенном выше «коде», если определено DefName, код выше $ Else компилируется.

    Delphi Version Symbols

    Распространенным применением директивы $ IfDef является тестирование версии компилятора Delphi.

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

    • УСЛОВНОЕ ОБОЗНАЧЕНИЕ — Компиляционная версия
    • VER80 — Delphi 1
    • VER90 — Delphi 2
    • VER100 — Delphi 3
    • VER120 — Delphi 4
    • VER130 — Delphi 5
    • VER140 — Delphi 6
    • VER150 — Delphi 7
    • VER160 — Delphi 8
    • VER170 — Delphi 2005
    • VER180 — Delphi 2006
    • VER180 — Delphi 2007
    • VER185 — Delphi 2007
    • VER200 — Delphi 2009
    • VER210 — Delphi 2010
    • VER220 — Delphi XE
    • VER230 — Delphi XE2
    • WIN32 — Указывает, что операционной средой является Win32 API.
    • LINUX — Указывает, что операционной средой является Linux
    • MSWindows — Указывает, что операционной средой является MS Windows / li]
    • ПРИСТАВКА — Указывает, что приложение компилируется как консольное приложение.

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

    Примечание: символ VER185, например, используется для обозначения компилятора Delphi 2007 или более ранней версии.

    Использование символов «VER»

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

    Например, функция IncludeTrailingBackslash, представленная в Delphi 5, добавляет «» в конец строки, если ее там еще нет. В проекте Delphi MP3 я использовал эту функцию, и несколько читателей пожаловались, что не могут скомпилировать проект — у них есть какая-то версия Delphi до Delphi 5.

    Одним из способов решения этой проблемы является создание собственной версии этой подпрограммы — функции AddLastBackSlash.

    Если проект должен быть скомпилирован на Delphi 5, вызывается IncludeTrailingBackslash. Если используются некоторые из предыдущих версий Delphi, мы моделируем функцию IncludeTrailingBackslash.

    Это может выглядеть примерно так:

    функция AddLastBackSlash (ул: строка) : строка; начать Результат: = IncludeTrailingBackslash (str); если Copy (str, Length (str), 1) = «» затем Результат: = ул еще Результат: = str + «»; конец;

    При вызове функции AddLastBackSlash Delphi выясняет, какая часть функции должна использоваться, а другая часть просто пропускается.

    Delphi 2008?

    Delphi 2007 использует VER180 для обеспечения неразрывной совместимости с Delphi 2006, а затем добавляет VER185 для разработки, которая по каким-либо причинам должна быть нацелена на Delphi 2007.

    Примечание. Каждый раз, когда интерфейс модуля изменяет код, использующий этот модуль, его необходимо перекомпилировать.Delphi 2007 — выпуск без прерываний, означающий, что файлы DCU из Delphi 2006 будут работать как есть.

    Директива компилятора Delphi для оценки аргументов в обратном порядке

    Я был очень впечатлен этим delphi двумя лайнерами, используя функцию IFThen из Math.pas. Однако сначала он оценивает DB.ReturnFieldI, что является неудачным, потому что мне нужно вызвать DB.first, чтобы получить первую запись.

    (как бессмысленное разъяснение, потому что у меня уже есть много хороших ответов. Я забыл упомянуть, что 0 — это код, который DB.First возвращает, если он что-то получил в нем, возможно, не имел смысла в противном случае)

    Очевидно, это не такая уж большая проблема, как я мог бы заставить ее работать с пятью прочными лайнерами. Но все, что мне нужно для этого, — это использовать Delphi для оценки DB.first сначала и DB.ReturnFieldI. Я не хочу менять math.pas, и я не думаю, что это гарантирует мне перегрузку, потому что там, как 16 функций.

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

    Обычно порядок оценки выражений undefined. (C и С++ аналогичны. Java всегда оценивает слева направо.) Компилятор не контролирует его. Если вам нужно два выражения для оценки в определенном порядке, напишите свой код по-разному. Я бы не стал беспокоиться о количестве строк кода. Линии дешевы; используйте столько, сколько вам нужно. Если вы часто используете этот шаблон, напишите функцию, которая завершает все:

    Ваш исходный код, вероятно, не был бы тем, что вы хотели, даже если порядок оценки был другим. Даже если DB.First не был равен нулю, вызов ReturnFieldI все равно будет оцениваться. Все фактические параметры полностью вычисляются перед вызовом функции, которая их использует.

    Изменение Math.pas все равно не поможет. Он не контролирует, в каком порядке оцениваются его фактические параметры. К тому времени, когда он их видит, они уже были оценены до логического значения и целого числа; они больше не являются исполняемыми выражениями.

    Вызывающее соглашение может повлиять на порядок оценки, но по-прежнему нет гарантии. Порядок, в который вставляются параметры в стек, не обязательно должен соответствовать порядку, в котором эти значения были определены. Действительно, если вы обнаружите, что stdcall или cdecl дает вам желаемый порядок оценки (слева направо), тогда они оцениваются в обратном порядке того, с которым они были переданы.

    Соглашение о вызове pascal передает аргументы слева направо в стеке. Это означает, что самый левый аргумент находится в нижней части стека, а самый правый аргумент находится вверху, чуть ниже адреса возврата. Если функция IfThen использовала этот вызов, существует несколько способов, которыми компилятор мог бы достичь этого макета стека:

    То, как вы ожидаете, это то, что каждый аргумент оценивается и сразу же перенаправляется:

    Оцените аргументы справа налево и сохраните результаты во временном режиме до тех пор, пока они не будут нажаты:

    Сначала выделите пространство стека и оцените в любом порядке:

    Обратите внимание, что IfThen принимает значения аргументов в том же порядке во всех трех случаях, но функции не обязательно вызывают в этом порядке.

    Соглашение о вызове по умолчанию также передает аргументы слева направо, но первые три аргумента, которые соответствуют, передаются в регистры. Однако регистры, используемые для передачи аргументов, также являются реестрами, наиболее часто используемыми для оценки промежуточных выражений. Результат DB.First = 0 необходимо было передать в регистр EAX, но компилятор также нуждался в этом регистре для вызова ReturnFieldI и для вызова First . Вероятно, было бы гораздо удобнее сначала оценить вторую функцию, например:

    Еще одно замечание: ваш первый аргумент является составным выражением. Там вызов функции и сравнение. Там нечего гарантировать, что эти две части выполняются последовательно. Компилятор может сначала вызвать вызовы функций, вызвав First и ReturnFieldI , а затем сравните возвращаемое значение First с нулем.

    Илон Маск рекомендует:  Что такое код gmp_jacobi
    Понравилась статья? Поделиться с друзьями:
    Кодинг, CSS и SQL