Имя любой переменной, объявленной в разделе 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.
Varch : 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
1B
Приведение типа 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;
VarI : 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
Здесь в левой части - печатаемые программой результаты (в десятичной системе счисления), в правой части - те же результаты в шестнадцатеричной системе счисления.
Полученные результаты четко показывают, что числовые данные располагаются в памяти таким образом, чтобы более старшие разряды находились в байтах с адресами большей величины.
Пусть для вещественной переменной R компилятор отвел поле памяти с адресом A. Так как переменная типа real занимает 6 байтов, то в поле R входят байты с адресами A+0, A+1, A+2, A+3, A+4, A+5. Тогда для R=5000 байты будут иметь следующее содержимое: