ГЛАВА 6. ВЫРАЖЕНИЯ. Выражения состоят из операторов и операндов. Большинство операторов в языке Паскаль являются бинарными, то есть содержат два операнда. Остальные операторы являются унарными и содержат только один операнд. В бинарных операторах используется обычное алгебраическое представление, например: A + B. В унарных операторах операция всегда предшествует операнду, например, -B. В более сложных выражениях порядок, в котором выполняются операции, соответствует приоритету операций (см. таблицу 6.1). Таблица 6.1. Приоритет операторов. ─────────────────────────────────────────────────────────────────── Операторы Приоритет Категория ─────────────────────────────────────────────────────────────────── @, not первый (высший) унарные операторы *,/,div,mod, второй операторы умножения and,shl,shr +,-,or,xor третий операторы сложения =, <>, <, >, четвертый операторы отношения <=, >=, in (низший) ─────────────────────────────────────────────────────────────────── Для определения старшинства операторов имеется три основных правила: 1. Операнд, находящийся между двумя операторами с различными приоритетами, связывается с оператором, имеющим более высокий приоритет. 2. Операнд, находящийся между двумя операторами с равными приоритетами, связывается с оператором, который находится слева от него. 3. Выражение, заключенное в скобки, перед выполнением вычисляется, как отдельный операнд. Операторы с равным приоритетом обычно выполняются слева направо, хотя иногда компилятор при генерации оптимального кода может переупорядочить операнды. Синтаксис выражений. Правила определяющие порядок выполнения операторов, вытекают из синтаксиса выражений, которые строятся из множителей, термов и простых выражений. Множитель имеет следующий синтаксис: ┌───────────────┐ множитель ───┬──┬───────────Ў│ссылка на пере-├───────────Ў │ │ ° │ менную │ ° │ │ │ └───────────────┘ │ │ │ ┌─┐ │ ┌─────────────┐ │ │ └─Ў│@│─┴──┬─Ў│идентификатор├────────┤ │ └─┘ │ │ процедуры │ │ │ │ └─────────────┘ │ │ │ ┌─────────────┐ │ │ └─Ў│идентификатор├────────┤ │ │ функции │ │ │ └─────────────┘ │ │ ┌─────────┐ │ ├────Ў│константа├──────────────────────┤ │ │без знака│ │ │ └─────────┘ │ │ ┌─┐ ┌─────────┐ ┌─┐ │ ├────Ў│(├──────Ў│выражение│────Ў│)├────┤ │ └─┘ └─────────┘ └─┘ │ │ ┌───┐ ┌─────────┐ │ ├────Ў│not├────Ў│множитель├────────────┤ │ └───┘ └─────────┘ │ │ ┌────┐ ┌─────────┐ │ ├────Ў│знак├───Ў│множитель├────────────┤ │ └────┘ └─────────┘ │ │ ┌─────────┐ │ ├────Ў│вызов ├──────────────────────┤ │ │функции │ │ │ └─────────┘ │ │ ┌─────────┐ │ ├────Ў│создание ├──────────────────────┤ │ │множества│ │ │ └─────────┘ │ │ ┌──────────────┐ │ └────Ў│приведение ├─────────────────┘ │типа значения │ └──────────────┘ Вызов функции активизирует функцию и представляет собой значения, возвращаемые функцией (см. далее в этой главе раздел "Вызовы функций"). Описатель множества представляет собой значение типа множества (см. раздел "Объявление множеств). Приведение типа изменяет тип значения (см. "Приведение типа"). Константа без знака имеет следующий синтаксис: ┌─────────┐ константа без знака ───┬────Ў│ число ├─────────────Ў │ │без знака│ ° │ └─────────┘ │ │ ┌──────────┐ │ ├────Ў│символьная├────────┤ │ │ строка │ │ │ └──────────┘ │ │ ┌─────────────┐ │ ├────Ў│идентификатор├─────┤ │ │ константы│ │ │ └─────────────┘ │ │ ┌───┐ │ └────Ў│nil├───────────────┘ └───┘ Некоторые примеры множителей могут включать в себя: X (ссылка на переменную) @X (указатель на переменную) 15 (константа без знака) (X + Z + 2) (подвыражение) sin(X/2) (вызов функции) ['0'..'9', 'A'..'Z'] (создание множества) not Done (отрицание логической переменной) Char(Digit + 48) (приведение типа) Термы используются в операциях умножения на множитель: ┌─────────┐ терм ────────Ў│множитель├───┬──────Ў ° └─────────┘ │ │ ┌───┐ │ ├─────┤ * │ў────────┤ │ └───┘ │ │ ┌───┐ │ ├─────┤ / │ў────────┤ │ └───┘ │ │ ┌───┐ │ ├─────┤div│ў────────│ │ └───┘ │ │ ┌───┐ │ ├─────┤mod│ў────────│ │ └───┘ │ │ ┌───┐ │ ├─────┤and│ў────────│ │ └───┘ │ │ ┌───┐ │ ├─────┤shl│ў────────│ │ └───┘ │ │ ┌───┐ │ └─────┤shr│ў────────┘ └───┘ Приведем несколько примеров термов: X*Y Z/(I-Z) Done or Error (X <= Z) and (Y < Z) В простых выражениях к термам применяются операторы сложения и присваивания знака. ┌───────┐ простое выражение ────────Ў│ терм ├───┬────Ў ° └───────┘ │ │ ┌───┐ │ ├─────┤ + │ў──────┤ │ └───┘ │ │ ┌───┐ │ ├─────┤ - │ў──────┤ │ └───┘ │ │ ┌───┐ │ ├─────┤ or│ў──────│ │ └───┘ │ │ ┌───┐ │ └─────┤xor│ў──────┘ └───┘ Приведем несколько примеров простых выражений: X + Y -X Hue1 + Hue2 I * J + 1 В выражениях к простым выражениям применяются операторы отношения: ┌─────────┐ выражение ────Ў│множитель├──┬───────────────────────────────Ў └─────────┘ │ ° │ ┌───┐ ┌─────────┐ │ ├─Ў│ < ├──────Ў│ простое ├──┘ │ └───┘ ° │выражение│ │ ┌───┐ │ └─────────┘ ├─Ў│<= ├──┤ │ └───┘ │ │ ┌───┐ │ ├─Ў│ > ├──┤ │ └───┘ │ │ ┌───┐ │ ├─Ў│>= ├──┤ │ └───┘ │ │ ┌───┐ │ ├─Ў│ = ├──┤ │ └───┘ │ │ ┌───┐ │ ├─Ў│<> ├──┤ │ └───┘ │ │ ┌───┐ │ └─Ў│in ├──┘ └───┘ Приведем некоторые примеры выражений: X = 1.5 Done <> Error (I < J) = (J < K) С in Нue1 Операторы. Операторы подразделяются на арифметические операторы, логические операторы, строковые операторы, операторы над множеством, операторы отношения и оператор @. Арифметические операторы. В следующей таблице приведены типы операндов и результаты для бинарных и унарных арифметических операций. Таблица 6.2. Бинарные арифметические операции. ─────────────────────────────────────────────────────────────────── Оператор Операция Тип операндов Тип результата ─────────────────────────────────────────────────────────────────── + сложение целый целый вещественный вещественный - вычитание целый целый вещественный вещественный * умножение целый целый вещественный вещественный / деление целый вещественный вещественный вещественный div целочислен- целый целый ное деление mod остаток целый целый ─────────────────────────────────────────────────────────────────── Примечание: Оператор + так же используется для работы со строками и множествами. Операторы +, - и * так же используются для работы с множествами. Таблица 6.3. Унарные арифметические операции. ─────────────────────────────────────────────────────────────────── Оператор Операция Тип операндов Тип результата ─────────────────────────────────────────────────────────────────── + сохранение целый целый знака вещественный вещественный - отрицание целый целый знака вещественный вещественный ─────────────────────────────────────────────────────────────────── Любой операнд, тип которого является поддиапазоном порядкового типа обрабатывается также, как если бы он был порядковый типа. Если оба операнда в операторах +, -, *, div или mod являются операндами целого типа, то тип результата будет таким же, как общий тип обоих операндов. (Объявление общего типа см. в разделе "Целый тип" главы 3). Если один или оба операнда в операторах +, -, или * имеют вещественный тип, то тип результата будет Real, если использована директива компилятора {$N-}, или типом Extended при использовании директивы компилятора {$N+}. Если при использовании оператора сохранения знака или оператора отрицания знака операнд имеет целый тип, то результат будет того же целого типа. Если операнд вещественного типа, то тип результата будет Real или Еxtended. Значение выражения X/Y всегда будет Real или Extended независимо от типов операндов. Если Y равно 0, то возникает ошибка. Значение выражения I div J представляет собой математическое частное от I/J, округленное в направлении к 0 до значения целого типа. Если J равно 0, то возникает ошибка. Оператор mod возвращает остаток, полученный при делении двух его операндов, то есть: I mod J = I - (I div J) * J Знак результата оператора mod будет тем же, что и знак I. Если J равно 0, то возникает ошибка. Логические операторы. Типы операндов и результатов логических операций показаны в таблице 6.4. Таблица 6.4. Логические операции. ─────────────────────────────────────────────────────────────────── Оператор Операция Тип операндов Тип результата ─────────────────────────────────────────────────────────────────── not битовое целый целый отрицание and И (битовое) целый целый or ИЛИ (битовое) целый целый xor исключающее целый целый ИЛИ (битовое) shl сдвиг влево целый целый shr сдвиг вправо целый целый ─────────────────────────────────────────────────────────────────── Примечание: Оператор not является унарным оператором. Если операндом оператора not является операнд целого типа, то результат будет такого же типа. Если оба операнда в операторах and, or или xor целого типа, то тип результата будет общим типом этих двух операндов. Операции I shl J и I shr J сдвигают значение I влево или вправо на J битов. Тип результата будет таким же, как тип I. Булевские операторы. Тип операндов и результатов для Boolean операций показаны в таблице 6.5. Таблица 6.5 Булевские операции. ─────────────────────────────────────────────────────────────────── Оператор Операция Тип операндов Тип результата ─────────────────────────────────────────────────────────────────── not отрицание Boolean Boolean and логическое И Boolean Boolean or логическое ИЛИ Boolean Boolean xor логическое Boolean Boolean исключающее ИЛИ ─────────────────────────────────────────────────────────────────── Примечание: оператор not является унарным оператором. Результаты этих операций соответствуют обычной булевой логике. Например, выражение А and В принимает значение True только в том случае, если оба операнда имееют значение True. В Turbo Pascal поддерживаются две различные модели генерации кода для операций or и and - полное вычисление и вычисление по короткой схеме (частичное вычисление). При полном вычислении подразумевается, что каждый операнд булевского выражения, построенный с помощью операторов or и and, всегда будет вычисляться, даже если результат всего выражения уже известен. Эта модель полезна в том случае, когда один или несколько операндов в выражении представляют собой функции с побочными эффектами, которые изменяют смысл программы. Вычисление по короткой схеме обеспечивает строгое вычисление слева направо. Это вычисление прекращается, как только результат всего выражения становится очевиден. Во многих случаях эта модель удобна, поскольку она обеспечивает минимальное время выполнения, и, как правило, минимальный объем кода. Вычисление по короткой схеме делает также возможным такие конструкции, которые в противном случае были бы недопустимы, например: while (I <= Length(S)) and (S[I] <> ' ') do Inc(I); while (P <> nil) and (P^.Value <> 5) do P := P^.Next; В обоих случаях, если результатом первого вычисления будет значение False, второе вычисление не выполняется. Моделью вычисления можно управлять с помощью директивы компилятора $B. Значением по умолчанию является состояние {$B-} (пока оно не будет изменено с помощью меню компилятора Options/Compiler), и в этом случае генерируется код с вычислением по короткой схеме. В случае директивы {$B+} генерируется код с полным вычислением. Поскольку в стандартном Паскале не определяется, какую модель следует использовать для вычисления булевских выражений, то программы, зависящие от действия какой-либо конкретной модели, в действительности не являются переносимыми. Однако, если пожертвовать переносимостью, то очень часто можно получить значительный выигрыш во времени выполнения и простоте, которую позволяет получить вычисление по короткой схеме. Строковые операторы. Типы операндов и результата для операций со строками показаны в таблице 6.6. Таблица 6.6. Операции со строками. ─────────────────────────────────────────────────────────────────── Оператор Операция Тип операнда Тип результата ─────────────────────────────────────────────────────────────────── + конкатенация строковый, строковый символьный или упакованный строковый ─────────────────────────────────────────────────────────────────── Turbo Pascal позволяет использовать оператор + для объединения двух строковых операндов. Результатом операции S+T, где S и T имеют строковый тип, символьный тип или упакованный строковый тип, будет конкатенация S и T. Результат будет совместим с любым строковым типом (но не с символьным и не с упакованным типом). Если длина результирующей строки превышает 255 символов, то она усекается до 255 символов. Операторы множества. Типы операндов для операций над множествами показаны в таблице 6.7. Таблица 6.7. Операции над множествами. ─────────────────────────────────────────────────────────────────── Оператор Операция Типы операндов ─────────────────────────────────────────────────────────────────── + объединение множества с совместимыми типами - разность множества с совместимыми типами * пересечение множества с совместимыми типами ─────────────────────────────────────────────────────────────────── Результаты операций над множествами подчиняются правилам логики множеств. - Порядковое значение С принадлежит А + В только в том случае, если с принадлежит А или В. - Порядковое значение С принадлежит А - В только в том случае, если С принадлежит А, но не принадлежит В. - Порядковое значение С принадлежит А * В только в том случае, если С принадлежит и множеству А, и множеству В. Если наименьшее порядковое значение, которое является результатом операции над множествами - это А, а наибольшее - В, то типом результата будет set of А..В. Операторы отношения. Типы операндов и результатов для операций отношения показаны в таблице 6.8. Таблица 6.8. Операции отношения. ─────────────────────────────────────────────────────────────────── Оператор Операция Тип операндов Тип результата ─────────────────────────────────────────────────────────────────── = Равно совместимый простой, Boolean указатель, множествен- ный, строковый или упа- кованный строковый <> Не равно совместимый простой, Boolean указатель, множествен- ный, строковый или упа- кованный строковый < Меньше совместимый простой, Boolean чем строковый или упа- кованный строковый > Больше совместимый простой, Boolean чем строковый или упа- кованный строковый <= Меньше совместимый простой, Boolean или строковый или упа- равно кованный строковый >= Больше совместимый простой, Boolean или строковый или упа- равно кованный строковый <= Подмно- совместимые типы Boolean жество множеств >= Надмно- совместимые типы Boolean жество множеств in Элемент левый операнд: лю- Boolean множества бой порядковый тип T; правый операнд: множество которого совместимо с T ─────────────────────────────────────────────────────────────────── Сравнение простых типов. Когда операнды =, <>, <, >, <=, >= простых типов, то это должны быть совместимых типов. Однако, если один операнд имеет действительный тип, то другой может быть целого типа. Сравнение строк. Операторы отношения =, <>, <, >, <=, или >= могут применяться для сравнения строк согласно порядку расширенного набора символов кода ASCII. Любые два значения строковых данных можно сравнить, поскольку все значения строковых данных совместимы. Значения символьного типа совместимы со значениями строкового типа, и при их сравнении символьное значение обрабатывается как строковое значение длиной 1. Когда со значением строкового типа сравнивается упакованное строковое значение из N элементов, то оно обрабатывается как значение строкового типа длиной N . Сравнение упакованных строк. Операторы отношения =, <>, <, >, <= или >= могут применяться также для двух упакованных значений строкового типа, если они содержат одинаковое число элементов. Если число элементов равно N, то операция соответствует сравнению двух строк, каждая из которых имеет длину N. Сравнение указателей. Для сравнения операндов типа указатель совместимых типов могут использоваться операторы = и <>. Два указателя равны только в том случае, если они ссылаются на один и тот же объект. Примечание. При сравнении указателей в Turbo Pascal просто сравниваются сегменты и смещения. В соответствии со схемой размещения сегментов процессоров 80х86 два логически различных указателя могут фактически указывать на одну и ту же физическую ячейку памяти. Например, Ptr($0040, $0049) и Ptr($0000, $0049) являются указателями с одинаковыми адресами. Указатели, возвращаемые стандартными процедурами New и GetMem, всегда нормализованны (смещение находится в диапазоне от $0000 до $000F) и, таким образом, всегда будут сравниваться корректно. При создании указателей с помощью стандартной функции Ptr и последующем сравнении таких указателей нужно особую осторожность. Сравнение множеств. Если операндами являются множества А и В, то при их сравнении получаются следующие результаты: - Выражение А = В истинно только тогда, когда А и В содержат одни и те же элементы, в противном случае А <> В. - Выражение А <= В истинно, если каждый элемент множества А также является элементом множества В. - Выражение А >= В истинно, когда каждый элемент множества В также является элементом множества А Проверка на принадлежность множеству. Оператор in возвращает истинное значение (True), когда значение элемента порядкового типа является элементом типа множества, в противном случае он возвращает значение False (ложное). Оператор @. С помощью оператора @ можно создать указатель на переменную. В таблице 6.9. показаны типы операнда и результатов. Таблица 6.9. Операция создания указателя. ─────────────────────────────────────────────────────────────────── Оператор Операция Тип операнда Тип результата ─────────────────────────────────────────────────────────────────── @ формирование ссылка на перемен- указатель (такой же указателя ную или процедуру как с nil) или идентификатор функции ─────────────────────────────────────────────────────────────────── Оператор @ является унарным оператором, в качестве операнда которого используется ссылка на идентификатор переменной, процедуры или функции; операнду возвращается указатель. Тип этого значения является таким же, как тип указателя nil, и, таким образом, его можно присвоить любому указателю. Примечание: для использования оператора @ для переменной типа процедура применяются особые правила. Для получения более полной информации обратитесь к разделу "Процедурные типы". Использование оператора @ для переменной. Использование оператора @ для обычной переменной (не параметра) не вызывает никаких затруднений. Введем объявления: type TwoChar = array [0 .. 1] of Char; var Int : Integer; TwoCharPtr : ^TwoChar; тогда утверждение TwoCharPtr := @Int; приводит к тому, что TwoCharPtr указывает на Int. TwoCharPtr^ становится повторной интерпретацией значения Int, как если бы она была символьным массивом array[0 .. 1] of Char. Использование оператора @ для параметра-значения. Использование операции @ для формального параметра-значения приводит к тому, что будет построен указатель, указывающий на ячейку стека, в которой содержится фактическое значение параметра. Предположим, что Foo является формальным параметром-значением процедуры, а FooPtr является переменной указателем. Если в процедуре выполняется операция: FooPtr := @Foo; то FooPtr^ будет ссылкой на значение Foo. Однако, FooPtr^ не указывает на сам параметр Foo, поскольку он указывает на значение Foo, которое было взято из Foo и сохранено в стеке. Использование оператора @ для параметра-переменной. Применение оператора @ к параметру-переменной приведет к тому, что будет сформирован указатель на фактический параметр (указатель берется из стека). Предположим, что One - параметр-переменная процедуры, Two - переменная, передаваемая в процедуру в качестве фактического параметра переменной One, а OnePtr является указателем на переменную. Если в процедуре выполняется оператор: OnePtr := @One; то OnePtr является указателем на переменную Two, а OnePtr^ - ссылка на саму переменную Two. Использование оператора @ для процедуры или функции. Вы можете применять оператор @ к процедуре или функции, при этом вы получите указатель на ее точку входа. В Turbo Pascal не предусмотрен механизм для использования такого указателя. Единственным применением указателя процедуры может быть передача его программе на языке ассемблер или использование в inline операторе. См. "Turbo Assembler и Turbo Pascal" в главе 23 для информации по интерфейсу Turbo Assembler с Turbo Pascal. Использование оператора @ с методом. Вы можете применить @ к уточненному идентификатору метода, чтобы создать указатель на точку входа метода. Вызов функции. Вызов функции приводит к активизации функции, заданной с помощью идентификатора функции. Идентификатором функции является любой идентификатор, использованный для обозначения функции. Если в соответствующем объявлении функции содержится список формальных параметров, то в вызове функции должен содержаться список фактических параметров. Каждый параметр подставляется вместо соответствующего формального параметра в соответствии с набором правил, который вводится в главе 19 "Ввод и вывод". ┌─────────────┐ вызов функции ───Ў│идентификатор├──┬────────────────────────────Ў │ функции │ │ ° └─────────────┘ │ ┌──────────────────┐ │ └──Ў│список фактических├──┘ │ параметров │ └──────────────────┘ ┌───┐ ┌───────────┐ ┌───┐ список фактических ─────Ў│ ( ├─────Ў│фактический├──┬─Ў│ ) ├───Ў параметров └───┘ ° │ параметр │ │ └───┘ │ └───────────┘ │ │ ┌───┐ │ └───┤ , │ў────────┘ └───┘ ┌────────────┐ фактический параметр ───┬──Ў│ выражение ├────────Ў │ └────────────┘ ° │ ┌────────────┐ │ └──Ў│ ссылка на ├───┘ │ переменную │ └────────────┘ Приведем некоторые примеры вызовов функций: Sum(A, 63) Maximum(147, J) Sin(X + Y) Eof(F) Volume(Radius, Height) Примечание: Функцию можно также вызвать и через переменную типа процедура. Более подробная информация содержится в разделе "Процедурные типы" главы 8. Синтаксис вызова функции расширен, чтобы разрешить уточненному идентификатору метода обозначать функцию для замены идентификатора функции. Обсуждение расширений операторов процедуры в разделе "Операторы процедуры" главы 7 так же применимо к вызовам функций. В режиме расширенного синтаксиса {$X+} вызов функции может использоваться как оператор; т.е. результат вызова функции может быть отброшен. (См. "Расширенный синтаксис" в главе 21.) Создание множеств. Создание множества определяет значения типа множество и получается путем записи выражений, заключенных в квадратные скобки []. Каждое выражение определяет значение множества. ┌───┐ ┌───┐ описатель ───Ў│ [ ├──┬────────────────────────Ў│ ] ├───Ў множества └───┘ │ ┌────────────┐ ° └───┘ └───Ў│ группа ├──┬─┘ ° │ элементов │ │ │ └────────────┘ │ │ ┌───┐ │ └────┤ , │ў────────┘ └───┘ ┌───────────┐ группа элементов ───Ў│ выражение ├──┬───────────────────────────Ў └───────────┘ │ ° │ ┌──┐ ┌───────────┐ │ └─Ў│..├──Ў│ выражение ├─┘ └──┘ └───────────┘ Обозначение [] обозначает пустое множество, тип которого совместим по присваиванию с типом любого множества. Любая группа элементов, объявленная, как X..Y, объявляет элементами множества все значения в диапазоне X..Y. Если X больше, чем Y, то X..Y не описывает никаких элементов и [X..Y] обозначает пустое множество. В конкретном описателе множества все значения выражений в группе элементов должны иметь один и тот же порядковый тип. Приведем некоторые примеры создания множеств: [Red, C, Green] [1, 5, 10..K mod 12, 23] ['A'..'Z', 'a'..'z', chr(Digit + 48)] Приведение типа. Тип выражения можно изменить на другой тип с помощью приведения типа. ┌─────────────┐ ┌───┐ ┌─────────┐ ┌───┐ приведение ───Ў│идентификатор├──Ў│ ( ├──Ў│выражение├──Ў│ ) ├──Ў значению типа │ типа │ └───┘ └─────────┘ └───┘ └─────────────┘ Тип выражения и задаваемый тип должны оба иметь порядковый тип или тип указателей. Для порядковых типов результирующее значение получается путем преобразования выражений. Преобразование может привести к уменьшению или увеличению размера исходного значения в том случае, если размер указанного типа отличается от размера типа выражения. В том случае, когда значение расширяется, его знак всегда сохраняется. Таким образом, значение всегда является расширяемым по знаку. Синтаксис приведения типа значения почти совпадает с синтаксисом приведения типа переменной (см. раздел "Приведение типа переменной" главы 4). Однако при приведении типа значения операции производятся со значениями, а не с переменными и, таким образом, могут не участвовать в ссылках на переменные, то есть за приведением типа значения не могут следовать квалификаторы. В частности, приведение типа значения не должны встречаться в левой части оператора присваивания. Некоторые примеры приведения типа значений: Integer('A') Char(48) Boolean(0) Color(2) Longint(@Buffer) BytePtr(Ptr($40, $49)) Процедурные типы в выражениях. Использование процедурной переменной в операторе или в выражении означает вызов процедуры или функции, хранящейся в переменной. Однако есть исключение: когда Turbo Pascal видит процедурную переменную в левой части оператора присваивания, он знает, что правая часть представляет процедурное значение. Например, рассмотрим программу: type IntFunc = function: Integer; var F: IntFunc; N: Integer; function ReadInt; var I: Integer; begin Read(I); ReadInt := I; end; begin F := ReadInt; N := ReadInt; end; Первый оператор в главной программе присваивает процедурное значение (адрес) ReadInt процедурной переменной F, а второй оператор вызывает ReadInt и присваивает возвращаемое значение в N. Различие между получением процедурного значения и вызовом функции производится по типу используемой переменной в операторе присваивания (F или N). К сожалению есть ситуации, где компилятор не может определить требуемое действие из контекста. Например, в следующем операторе нет очевидного способа узнать должно ли сравниваться процедурное значение в F с процедурным значением в ReadInt, или определить, указывает ли F на ReadInt, или должен быть вызов F и ReadInt, а затем произвести сравнение возвращенных значений. if F = ReadInt then Writeln('Equal'); Однако, синтаксис стандартного Паскаля указывает, что появление идентификатора функции в выражении означает вызов этой функции, так что эффект предыдущего оператора - это вызов F и ReadInt и сравнение возвращенных значений. Чтобы сравнить процедурное значение в F с процедурным значением в ReadInt, должна использоваться конструкция: if @F = @ReadInt then Writeln('Equal'); При использовании с процедурной переменной или идентификатором процедуры или функции, оператор адреса @ предотвращает компилятор от вызова процедуры и преобразует аргумент в указатель. Так @F преобразует F в переменную нетипированного указателя, содержащую адрес, а @ReadInt возвращает адрес ReadInt; затем 2 значения указателей можно сравнить для определения, ссылается ли F на ReadInt. Чтобы получить адрес процедурной переменной, а не адрес, хранимый в ней, нужно использовать двойной оператор адреса @@. Например, @P преобразует P в переменную нетипированного указателя, а @@P возвращает адрес переменной Р.