- •1. Двоичная система счисления.
- •2. Восьмеричная система счисления.
- •3. Шестнадцатеричная система счисления.
- •4. Сложение и вычитание в 2, 8 и 16 c/c.
- •2. Вещественные числа (числа с плавающей запятой).
- •3. Логические данные.
- •2. Зарезервированные слова.
- •X a8 alpha Massiv z52d9 eps Res_52_a ___75
- •6. Метка.
- •2. Целые типы данных.
- •4. Вещественные типы.
- •1. Раздел описания меток.
- •2. Раздел описания констант.
- •3. Раздел описания типов.
- •4. Раздел описания переменных.
- •6. Раздел операторов.
- •7. Последовательность разделов.
- •1. Формульно-словесный способ.
- •2. Блок-схемный способ.
- •Ввод - вывод одномерного массива
- •2. Ввод массива из текстового файла.
- •3. Вывод одномерного массива на экран.
- •Примеры обработки одномерных массивов
- •1. Параметр цикла должен быть ординального типа.
- •2. Параметр должен быть описан в том же блоке, где находится сам оператор цикла.
- •5. В теле цикла параметр не должен изменяться.
- •6. Начальное и конечное значения параметра цикла вычисляются только один раз, до начала цикла.
- •7. При нормальном завершении цикла значение его параметра считается неопределенным.
- •Контроль ординальных переменных
- •Вставка элемента в упорядоченный массив
- •Удаление элементов из массива
- •«Школьный» алгоритм сортировки
- •Группировка массива методом прямой выборки
- •Группировка массива методом прямого обмена
- •Var c : array[1..10,1..15,1..8] of real.
- •1. Ввод элементов матрицы с клавиатуры.
- •2. Ввод матрицы из текстового файла.
- •3. Вывод матрицы на экран.
- •Тождественные и совместимые типы
- •Обработка в процедуре одномерных массивов с различными именами типов
- •Обработка в процедуре матриц с различными именами типов
- •Var s : string[V],
- •Процедуры и функции для обработки строк
- •Определение битовой структуры поля памяти
- •Процедуры и функции для файлов любого типа
- •Var p : pointer;
- •1. Формирование стека из текстового файла.
- •7. Определение значения и местоположения максимального элемента в стеке.
- •8. Удаление из стека максимального элемента.
- •9. Добавление элемента в упорядоченный стек.
- •2. Добавление нового элемента в очередь.
- •3. Удаление элемента из очереди.
- •6. Удаление произвольного элемента из очереди.
- •7. Добавление нового элемента в произвольное место очереди.
- •1. Формирование дека.
- •Var sin : integer;
- •Процедура заполнения FillChar
- •Процедура перемещения данных move
- •Управление экраном в текстовом режиме
- •Сохранение и восстановление экрана
- •Interface
- •Implementation
- •Процедуры управления текстовым режимом экрана
- •Intr(n:byte; Var Reg:Registers),
- •If KeyPressed then
- •Автоматическая оптимизация программ
- •1. Свертывание констант.
- •2. Слияние констант.
- •3. Вычисление по короткой схеме.
- •4. Удаление неиспользуемого кода.
- •If false then
- •5. Эффективная компоновка.
- •Оверлейная структура программы
- •Interface
- •Implementation
- •Interface
- •Implementation
- •Использование сопроцессора
Тождественные и совместимые типы
При передаче и преобразовании информации в программе должны соблюдаться некоторые требования, блокирующие некорректные действия программиста. Эти требования могут быть разделены на три группы, соответствующие следующим случаям:
передача фактического параметра-переменной;
вычисление выражения;
выполнение оператора присваивания и передача фактического параметра-значения.
В первом случае должны быть выполнены требования тождественности типов, во втором – совместимости типов, в третьем – совместимости по присваиванию.
Тождественность типов.
Рассмотрим процедуру, в списке формальных параметров которой заданы лишь параметры-переменные (перед именем формального параметра записано слово Var).
Пример 1.
Type Ar1 = array[1..100] of real;
Ar2 = array[1..100] of real;
Ar3 = Ar1;
Ar4 = Ar3;
Var X : Ar1; Y : Ar2; Z : Ar3; W : Ar4;
a,b : real;
m,n : integer;
Procedure Proc1(Var D:Ar1; Var k:integer);
Begin
..........................
End { Proc1 };
Ранее было указано, что формальный и соответствующий ему фактический параметр должны иметь одно и то же имя типа. Это не совсем точно.
Обозначим через Type1 имя типа формального параметра, через Type2 - имя типа соответствующего ему фактического параметра.
Как известно, при обращении к процедуре фиктивный адрес формального параметра-переменной замещается реальным адресом фактического параметра. В этом случае формальная и фактическая переменные соответствуют одному и тому же полю памяти. Следовательно, имена типов Type1 и Type2 этих переменных должны определять переменные одинакового размера и структуры, с одинаковым множеством допустимых значений и операций по их обработке. Последнее возможно, если типы Type1 и Type2 тождественны.
Два типа считаются тождественными, если они представляют собой одно и то же имя типа или один из них описан как эквивалентный другому типу.
В примере 1 типы Ar1, Ar3 и Ar4 тождественны, Ar1 и Ar2 - не тождественны, хотя они и имеют одинаковое описание типа. Поэтому обращения к процедуре Proc1(X,m), Proc1(Z,m) и Proc1(W,m) считаются правильными, а при обращении Proc1(Y,m) будет выведено сообщение "Type mismatch" ("Несоответствие типов").
Примечание. Вполне очевидно, что сказанное выше в равной мере относится также к параметрам-константам.
Совместимость типов. Рассмотрим пример 2.
Пример 2.
Var x,y : real;
m,n : integer;
ch : char;
b : boolean;
Begin ...............
y:=x+2*ch;
b:=(x<ch) and n;
Вполне очевидно, что выражения в правой части операторов присваивания не могут быть вычислены, поскольку лишены смысла арифметические операции по отношению к символьным переменным, сравнение числовых и символьных переменных и т.п. В этих выражениях нарушены требования совместимости типов.
Основные правила совместимости типов:
- операнды выражения имеют численные типы (вещественный, целочисленный, диапазонный);
- операнды определены логическим типом;
- операнды имеют строковый, символьный или диапазонный символьный типы.
Если операнды выражения имеют различные численные типы, то при выполнении арифметических операций производится преобразование их значений к более старшему типу в соответствии со следующими приоритетами:
- real;
- longint;
- integer, word;
- shortint, byte.
Совместимость типов по присваиванию. Рассмотрим пример 3.
Пример 3.
Var x,y : real;
m,n : integer;
ch : char;
b : boolean;
Begin ...............
{ 1 } y:=(m<n) and b;
{ 2 } ch:=x+m;
{ 3 } m:=2*x+y;
{ 4 } y:=3*m-n;
Числовой переменной нельзя присвоить булевское значение, символьной переменной - численное значение. Поэтому операторы 1 и 2 не могут быть выполнены, здесь нарушается требование совместимости по присваиванию.
В правой части оператора 3 - вещественное значение, в левой части - целочисленная переменная. Если допустить выполнение такого оператора, то дробная часть вещественного значения должна быть отброшена, т.е. произошла бы потеря точности. В связи с этим считается, что в операторе 3 также нарушаются требования совместимости по присваиванию.
В операторе 4 потеря точности не наблюдается. При его выполнении производится лишь преобразование целочисленного значения, полученного при вычислении выражения в правой части оператора, к типу real.
Обозначим тип переменной в левой части Type1, тип значения выражения - Type2.
Основные требования совместимости по присваиванию:
- Type1 и Type2 имеют тождественные типы и ни один из них не является файловым типом;
- Type1 и Type2 - целочисленные типы;
- Type1 - вещественный тип, Type2 - вещественный или целочисленный тип;
- Type1 и Type2 - строковые типы;
- Type1 - строковый тип, Type2 - символьный тип.
Более жесткие требования должны соблюдаться, когда в левой и правой частях оператора присваивания записаны составные переменные, в частности, массивы.
Пример 4.
Type Ar1 = array[1..100] of real;
Ar2 = array[1..100] of real;
Ar3 = Ar1;
Var X,Y : Ar1; Z : Ar2; W : Ar3;
Здесь типы Type1 и Type2 должны быть тождественными. Следовательно, в этом случае операторы Y:=X и Y:=W являются допустимыми, в то время как для оператора Y:=Z будет определено несоответствие типов.
Рассмотрим теперь процедуру, в списке формальных параметров которой имеются параметры-значения.
Пример 5.
Var x,y : real;
m,n : integer;
Procedure Proc2(k:integer; r,t:real);
Begin
.............
End { Proc2 };
Begin
.............
{ 1 } Proc2(m,n-1,x+m);
{ 2 } Proc2(y,x+y,x);
Для параметра-значения в теле процедуры выделяется поле памяти в соответствии с его типом. При обращении к процедуре в это поле пересылается значение фактического параметра; другими словами, формальному параметру присваивается значение фактического параметра. Следовательно, по отношению к параметру-значению должны соблюдаться изложенные выше требования совместимости по присваиванию. В частности, в примере 5 для оператора 1 эти требования соблюдаются, для оператора 2 - не соблюдаются.
П Р И В Е Д Е Н И Е Т И П О В П Е Р Е М Е Н Н Ы Х
Имя любой переменной, объявленной в разделе Var, - это адрес поля памяти в машинной программе. Оператор присваивания a:=b, рассматриваемый на уровне машинной программы, - это пересылка содержимого поля памяти b в поле памяти a.
При программировании на машинном языке или на языке ассемблера произвольному полю памяти можно присвоить значение любого другого поля памяти. В этом случае ответственность за правильность выполнения оператора присваивания полностью возлагается на программиста.
Иная ситуация имеет место при программировании на языках высокого уровня, в частности на языке Паскаль. Имя переменной, идентифицирующей адрес поля памяти, всегда имеет вполне определенный тип. Тип переменной однозначно определяет множество значений, которые имеет право принимать переменная данного типа, и набор операций, которые допустимы при ее обработке.
Пример.
Var k,m : byte;
p,q : char;
Begin
k:=65; p:='A';
1) m:=k; q:=p;
2) m:=p; q:=k;
3) m:=ord(p); q:=chr(k);
Внутреннее представление переменной k и переменной p совершенно одинаково (порядковый номер символа 'A' в таблице ASCII равен 65) и имеет вид 0110 0101.
Действие оператора m:=k сводится к пересылке содержимого однобайтного поля k в поле m. Аналогичные действия выполняются для оператора q := p. В то же время операторы m:=p и q:=k не могут быть выполнены, так как при их трансляции будет обнаружено несоответствие типов.
Пересылка содержимого поля p в поле m может быть выполнена оператором m:=ord(p). Функция ord не производит никаких преобразований значения переменной p, она лишь информирует транслятор, что значение этой переменной следует рассматривать как значение целочисленной переменной и переслать соответственно в поле m. Аналогичная ситуация имеет место для оператора q:=chr(k). Следовательно, функции ord и chr выполняют приведение типа, а не преобразование значений переменных.
При программировании на Паскале может возникать необходимость рассматривать одно и то же поле памяти как значения переменных самых различных типов, а не только типов char и byte. В этом случае используют аппарат приведения типа переменных, с помощью которого обращение к переменной одного типа может рассматриваться как обращение к переменной другого типа.
Синтаксис приведения типа:
Здесь поле памяти, определяемое идентификатором переменной, рассматривается как экземпляр типа, представленного идентификатором типа.
Рассмотрим суть приведения типов в Турбо Паскале на следующих трех примерах.
Пример 1.
Var ch : char;
b1,b2 : byte;
Begin
ch:='A'; b1:=byte(ch); b2:=ord(ch);
Writeln('b1=',b1,' b2=',b2);
Будет отпечатано:
b1=65 b2=65
Выражение byte(ch) определяет "наложение" типа byte на переменную ch. В этом случае биты, содержащиеся в поле памяти ch, интерпретируются как значение переменной типа byte (ch = 010000012 = 4116 = 6510).
Примечание. Внешняя форма записи выражения byte(ch) и функции ord(ch) одинакова, что позволяет трактовать выражение byte(ch) как функцию приведения к типу byte.
Пример 2.
Type ByteAr = array[1..2] of byte;
Var W : word;
Begin
ByteAr(W)[1]:=10; ByteAr(W)[2]:=20;
Writeln(W,' ',ByteAr(W)[1],' ',ByteAr(W)[2]);
W:=2500;
Writeln(W,' ',ByteAr(W)[1],' ',ByteAr(W)[2]);
В результате работы программы будет отпечатано:
5130 10 20
2500 196 9
B примере 2 происходит наложение "формального" поля ByteAr длиной два байта на реальное поле W, также имеющее длину два байта. При этом байты, входящие в состав поля W, рассматриваются как отдельные элементы байтового массива типа ByteAr.
Отпечатанные выше элементы в 16 с/с имеют следующие значения:
140A OA 14
09C4 C4 09
Cоответствие между элементами полей W и ByteAr легко просматривается, если учесть, что в переменной типа word старшим считается правый байт. Последнее определяется тем, что в процессорах типа Intel числовое значение размещается в поле памяти таким образом, что более старшие разряды числа располагаются в байтах с более старшими адресами. Это положение иллюстрируется ниже на примере переменной k типа longint.
Переменной k выделяется 4 байта памяти. Поскольку адресом поля памяти является адрес его крайнего левого байта, то байты поля k получат следующие адреса:
k + 0 k + 1 k + 2 k + 3
|
|
|
|
Пусть k = 2 000 100 101 = $ 77 37 1B 05. Тогда в байтах с адресами k+0 .. k+3 шестнадцатеричные цифры значения переменной k будут расположены следующим образом:
k + 0 k + 1 k + 2 k + 3
05 |
1B |
37 |
77 |
Приведение типа T(v), где T - имя типа, v - имя переменной, позволяет трактовать на машинном уровне биты, содержащиеся в поле v, как биты, принадлежащие значению типа T.
Пример 3. Определить численные значения байтов, входящих в состав полей памяти типов integer и real. Здесь фактически идет речь об определении внутренного представления переменных типов integer и real.
Program PutType;
Type ByteAr2 = array[1..2] of byte;
ByteAr6 = array[1..6] of byte;
Var I : integer;
R : real;
Begin
I:=5000; R:=5000;
Writeln(' I: ',ByteAr2(I)[1],' ',ByteAr2(I)[2]);
Writeln(' R: ',ByteAr6(R)[1],' ',ByteAr6(R)[2],
' ',ByteAr6(R)[3],' ',ByteAr6(R)[4],
' ',ByteAr6(R)[5],' ',ByteAr6(R)[6]);
I:=-5000; R:=-5000;
Writeln(' I: ',ByteAr2(I)[1],' ',ByteAr2(I)[2]);
Writeln(' R: ',ByteAr6(R)[1],' ',ByteAr6(R)[2],
' ',ByteAr6(R)[3],' ',ByteAr6(R)[4],
' ',ByteAr6(R)[5],' ',ByteAr6(R)[6]);
End.
Результаты работы программы удобно представить в следующем виде:
I: 136 19 I: 88 13
R: 141 0 0 0 64 28 R: 8D 00 00 00 40 1C
I: 120 236 I: 78 EC
R: 141 0 0 0 64 156 R: 8D 00 00 00 40 9C
Здесь в левой части - печатаемые программой результаты (в десятичной системе счисления), в правой части - те же результаты в шестнадцатеричной системе счисления.
Проверка результатов:
5000 = $1388 ; -5000 = $EC78 ;
1388 = 0,1388 164 = 0, 0001 0011 1000 1000 216 = 0,1001110001000 213
Значение R в 16 с/c : 1C 40 00 00 00 8D.
Полученные результаты четко показывают, что числовые данные располагаются в памяти таким образом, чтобы более старшие разряды находились в байтах с адресами большей величины.
Пусть для вещественной переменной R компилятор отвел поле памяти с адресом A. Так как переменная типа real занимает 6 байтов, то в поле R входят байты с адресами A+0, A+1, A+2, A+3, A+4, A+5. Тогда для R=5000 байты будут иметь следующее содержимое:
(A+0) = 8D ; (A+1) = 00 ; (A+2) = 00 ; (A+3) = 00 ; (A+4) = 40 ; (A+5) = 1C .
Пример 4.
Var x : real;
y : byte;
Вместо условного оператора
If x<0 then
y:=1
Else
y:=0;
можно написать
y:=byte(x<0);
А Б С О Л Ю Т Н Ы Е П Е Р Е М Е Н Н Ы Е
Как известно, память для переменных, объявленных в разделе Var, выделяется при старте блока, в котором содержится данный раздел, в порядке объявления имен переменных. Местоположение блока в пространстве оперативной памяти ЭВМ определяет операционная система. В то же время Турбо Паскаль предоставляет возможность программисту так описать переменные, чтобы им были назначены заранее заданные адреса. Такие переменные называются абсолютными; их описание после имени типа должно содержать так называемое абсолютное предложение.
Синтаксис абсолютного предложения:
Структура абсолютного предложения имеет два альтернативных варианта.
В первом варианте производится задание абсолютного адреса в форме, принятой в операционной системе MS DOS (адрес сегмента и смещение - см.раздел "Адресация операндов").
Пример 1.
Type ScreenChar = record
Symbol : char;
Attrib : byte;
end;
ScreenAr = array[1..25,1..80] of ScreenChar;
Var y : real absolute $A100:$0000;
Screen : ScreenAr absolute $B800:$0000;
Здесь переменным y и Screen назначаются конкретные адреса оперативной памяти. В частности, значение $B800:$0000 определяет фиксированный адрес видеопамяти.
Примечание. В конструкции record .. end объединяются переменные различного типа (см.раздел "Записи").
Во втором варианте для абсолютной переменной назначается адрес другой, ранее объявленной переменной.
Пример 2 (аналогично примеру 2 раздела "Приведение типов переменных").
Type ByteAr = array[1..2] of byte;
Var W : word;
Br : ByteAr absolute W;
Begin
Br[1]:=10; Br[2]:=20;
Writeln(W,' ',Br[1],' ',Br[2]);
W:=2500;
Writeln(W,' ',Br[1],' ',Br[2]);
В результате работы программы будет отпечатано:
5130 10 20
2500 196 9
Переменные W и Br адресуют одно и то же поле памяти. Другими словами, одно и то же поле памяти размером два байта имеет два имени: имя W и имя Br. Если в программе используется имя W, то это поле можно обрабатывать как переменную типа word, для имени Br допустимые операции определяются именем типа ByteAr.
При совмещении переменных с помощью директивы absolute не рекомендуется в разделе Var использовать описание типа вместо имени типа. Нарушение этого правила не анализируется компилятором, но может привести к неправильной работе программы, вплоть до ее "зависания", особенно при использовании сопроцессора.
Например, вместо описания
Var A : array[1..6] of longint;
B : array[1..4] of real absolute A;
следует применять описание
Type ArLong = array[1..6] of longint;
ArReal = array[1..4] of real;
Var A : ArLong;
B : ArReal absolute A;