У вас вопросы?
У нас ответы:) SamZan.net

Кардинальное число типа Pointer соответвует 4 байтам и включает значение nil

Работа добавлена на сайт samzan.net: 2016-03-30

Поможем написать учебную работу

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

Предоплата всего

от 25%

Подписываем

договор

Выберите тип работы:

Скидка 25% при заказе до 14.3.2025

Тип  pointer

Встроенный тип  pointer  обозначает  указатель, который в отличие от понятия ссылки в динамических структурах, не  связан конструкцией Record с ни с каким определенным типом данных. Кардинальное число типа Pointer соответвует 4 байтам и включает значение nil. При этом указатели, если они не являются полем в рекурсивных структурах данных, размещаются там же, где и статические переменные, т. е. в сегменте данных.

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

type

DynType=array [1..$FFF0] of Char;

var

BufferPtr : ^ DynType; {массив критического для сегмента размера

теперь будет размещаться в Heap-области}

StrPtr :^ String;

.  .  .

Перед использованием переменных типа pointer их, естественно, нужно инициализировать, т. е. выделить память для распределения значения соответствующего типа данных (полная аналогия с формированием новой компоненты динамических структур данных). Для этих целей используется уже известная процедура new с единственным параметром в виде переменной- указателя:

.  .  .

New(BufferPtr);

New(StrPtr);

.  .  .

В соответствии с ранее описанными для ссылки правилами, обращение к таким переменным должно иметь вид:

.  .  .

BufferPtr^[3] :=‘a’;

StrPtr ^:=‘Иванов’;

.  .  .

В таком контексте динамические переменные ничем не отличаются от обычных переменных соответствующих типов: к ним применимы все допустимые над типом операции.

Процедура Dispose. Кроме процедуры New для работы с динамическими переменными в Borland Pascal зарезервирована процедура Dispose с тем же параметром. Она предназначена для освобождения памяти в Heap, выделенной процедурой New. Так после вызовов:

.  .  .

Dispose(BufferPtr);

Dispose(StrPtr);

.  .  .

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

выделение памяти (инициализация с помощью New);

работа с соответствующей переменной;

освобождение памяти с помощью Dispose.

Описанная семантика процедур New и Dispose этим не ограничивается и дополнительно расширена. Ее “функциональная” форма могут использоваться по отношению к любым динамически размещаемым переменным. процедура new в расширенной трактовке может использоваться как функция, возвращающая значения указателя, т. е. возможны присваивания вида:

type

ArcPtr =^Arc;

var

PArc : ArcPtr;

begin  

PArc :=New(ArcPtr);

Процедуры Mark и Release. Существует еще один способ динамического размещения переменных. Его основу составляет несколько иной подход к выделению памяти в Неар. Для области Неар в модуле System выделено несколько ключевых переменных-указателей (см. таблицу): HeapOrg, HeapPtr и HeapEnd. Переменная HeapOrg всегда указывает на начало области Неар, переменная HeapEnd указывает на конец области, а переменная HeapPtr содержит указатель на начало нераспределенной памяти в Неар. Естественно, если значение HeapPtr равно значению HeapOrg, то это говорит о том, что область Неар пуста, а если HeapPtr равно HeapEnd, то полностью занята. Любое выделение памяти в Неар приводит к увеличению значениия HeapPtr.

Процедура Mark(var P:Pointer) записывает текущее значение HeapPtr в переменную-указатель Р, тем самым фиксируя текущее состояние Неар. С помощью процедуры Release(var P:Pointer) в области Неар автоматически освобождаются все динамические переменные, распределенные выше указателя Р. При этом текущее значение HeapPtr станет равным Р. Вызов процедуры Mark всегда должен предшествовать вызову процедуры Release. В примере использования Mark и Release задействованно обращение к функции MemAvail, которая возвращает размер свободной памяти в Неар.

var

HeapTop : ^Word;

. . .

begin

Mark(HeapTop);

WriteLn(‘Размер памяти в Heap:’,MemAvail);

New(RealP);

WriteLn(‘Heap после размещения RealP^:’,MemAvail);

New(NameStrP);

WriteLn(‘Heap после размещения NameStrP^:’,MemAvail);

Release(HeapTop);

WriteLn(‘Heap после Release:’,MemAvail)

end.

Процедуры GetMem и FreeMem. Для динамического распределения памяти в Неар служат еще две тесно взаимосвязанные процедуры. Подобно New и Dispose, они во время вызова выделяют и освобождают память для одной динамической переменной. Процедура GetMem(var P: Pointer; Size: Word) создает в Неар новую динамическую переменную Р^ с определенным размером Size. Переменная-указатель Р может указывать на любой допустимый тип. Процедура FreeMem(var P: Pointer; Size: Word) освобождает динамическую переменную заданого размера.

Если в программе используется этот способ распределения памяти, то вызовы GetMem и FreeMem должны соответствовать друг другу, а значения Size при обращении к одной и той же переменной-указателю должны совпадать. Обращения к GetMem и FreeMem могут полностью соответствовать вызовам New и Dispose. При этом удобно использовать функцию Sizeof, которая возвращает размер памяти, требуемый для размещения значения заданного типа:

New(NameStr);

Dispose(NameStr);

GetMem(NameStrP,Sizeof(NameStr));  {будет тот же результат,}

FreeMem(NameStrP,Sizeof(NameStr)); {что для New и Dispose.}

С помощью процедур GetMem и FreeMem одной переменной-указателю можно выделить разное количество памяти в зависимости от потребностей. В этом заключено основное отличие между ними и процедурами New и Dispose:

GetMem(HeapTop, 40);  {выделено 40 байт памяти для HeapTop}

.  .  .

FreeMem(HeapTop, 40);

GetMem(HeapTop, 2000);  {выделено 2000 байт памяти для HeapTop}

. . .

FreeMem(HeapTop, 2000);

 

Операции со ссылками 

Операциями, допустимыми применительно к переменными ссылочного типа в Стандарте языка, являются операция присваивания, т.е. настройка ссылки на некоторый объект (или настройка на фиктивный объект, если ссылке дается значение nil) и операции отношения. Такой подход представляется разумным, поскольку другие операции над ссылками в контексте рекурсивных структур данных бессмысленны. Однако с учетом введения в средства языка стандартного типа pointer, этот набор расширен операцией взятия адреса –@.

Операция @ возвращает адрес переменной, т. е. строит значение-указатель, ссылающееся на эту переменную, например:

type

TChar = array[0..1] of Char;

var

Int: Integer;

TCharPtr : ^TChar;

. . .

тогда оператор

TCharPtr := @ Int ;

приводит к тому, что значением TCharPtr становится значение адреса переменной Int, несмотря на объявление TCharPtr : ^TChar. 

Тип получаемого в результате применения операции @ указателя управляется директивой компилятора $T: в состоянии $T- (по умолчанию) типом результата будет pointer, т. е.  нетипизированный указатель, совместимый со всеми другими типами указателей. В состоянии $T+ типом результата будет ^T, где T – тип ссылки на переменную (тип результата будет совместим со всеми другими указателями на тип этой переменной).

Процедурный тип данных

Как уже упоминалось, версии языка, разрабатываемые фирмой Borland по отношению к Стандарту, содержат ряд расширений. Рассмотренные ранее расширения, включая средства модульного конструирования программ, тип указателя (pointer) и строковый тип (string) можно отнести к таким, которые в целом не изменяют основных концепций авторской версии языка.

Введение в средства языка, начиная с пятой версии Borland Pascal, процедурного типа данных следует отнести к существенным расширениям. Этот тип выходит за рамки первоначальной концепции данных и позволяет интерпретировать процедуры и функции как объекты, которые можно использовать при определении переменных и передаче параметров. Значениями процедурного типа являются целые числа в диапазоне, который соответствует значениям адресов вызова процедур и функций (точнее области памяти, в которой предусмотрено их размещение), а также значение nil. Поэтому для данных процедурного типа определение кардинального числа теряет смысл, а какие либо операции над данными этого типа кроме операции присваивания недопустимы.

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

Описание процедурного типа выполняется в разделе типов, т. е. соответствует принятому синтаксису языка, например:

type

Proc=procedure;

SwapProc=procedure(var X,Y : Integer);

StrProc=procedure(Str : String);

ReadProc = procedure (var S : String);

MathFunc=function(X : Real) : Real;

DeviceFunc=function(var F : Text) : Integer;

MaxFunc=function(A,B : Real; F: MathFunc) : Real;

Как видно из примеров, в описании процедурного типа используются зарезервированные слова procedure и function и приводится полное описание параметров, а в случае функции указываются и тип результата. Иными словами, синтаксис описания процедурного типа соответствует синтаксису обычного заголовка процедуры либо функции с той лишь разницей, что опускается их имя. Имена формальных параметров при этом так же играют играют чисто символическую роль и присутствуют в описании только для указания их количества и типа. Таким образом:

<процедурный тип>   ::= procedure(<список  формальных параметров>)÷ 

                                      function(<список формальных параметров>) : <тип>

В приведенном выше примере при описании типа MaxFunc в качестве параметра используется процедурный тип MathFunc.

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

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

var

P : SwapProc;

F: MathFunc;

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

type 

SwapProc = procedure(var X, Y : Integer);

MathFunc = function(A,B : Real);

var

P: SwapProc;

F: MathFunc;

procedure Swap(var A,B : Integer); far;

var 

Buf : integer;

begin

Buf := a;

A := B;

B := Buf;

end; {Swap}

function Tan(Angle : real); far;

begin

Tan := Sin(Angle) / Cos(Angle);

end; {Tan}

begin

. . .

P := Swap;

F := Tan;

P(I, J);      {эквивалентно Swap(I, J)}

X :=F(X); {эквивалентно X :=Tan(x)}

. . .

Для обеспечения совместимости по присваиванию процедура либо функция в правой части от знака присваивания должна удовлетворять следующим требованиям:

описываться с директивой far и компилироваться с опцией $f+;

íå ìîæåò ÿâëÿòüñÿ ñòàíäàðòíîé ïðîöåäóðîé èëè ôóíêöèåé ÿçûêà;

íå äîëæíà áûòü ïðîöåäóðîé òèïà inline èëè interrupt, ò. å. ïðåäñòàâëÿòü ñîáîé êîäîâóþ âñòàâêó.

Первое из  этих ограничений связано с тем, что процедурный тип по представлению совместим с типом pointer. Переменным этого типа в версиях  Borland Pascal соответствует адресное пространство в 4 байта длиной, задающее адрес объекта данных в виде <сегмент><смещение>. Для переменных процедурного типа, как уже упоминалось, этот адрес указывает на место в памяти, где находится выполнимый код соответствующей процедуры либо функции. По этой причине при компиляции программ должна быть использована, где это необходимо, директива ($f+) для формирования far-адресации <сегмент><смещение> при последующих вызовах объектов процедурного типа. То же самое можно сказать о вложенных в процедурный тип процедурах и функциях, которые при компиляции располагаются в сегменте памяти машины, зарезервированном под стек, и имеют near - адресацию. Более подробно с директивами, управляющими в этом случае процессом трансляции, можно ознакомиться с помощью  фирменной документации для используемой версии системы программирования.

Чтобы иметь возможность присваивать переменным процедурного типа значения, представляющие собой стандартные функции, необходимо создавать специальную "оболочку". Например, стандартная функция sin(x), взятая в оболочку FSin совместима по присваиванию с описанным выше типом MathFunc:

function FSin(X : real): Real; far;

begin

FSin :=sin(X);

 end;

Îáîëî÷êà äëÿ ïðîöåäóðû Read может иметь вид:

tуре

($f+) {заменяет директиву far}

procedure MyRead (var S : String); {по типу совместима с ReadProc}

begin

Read (S);                                  

 end;

($f-)

. . .

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

Использование процедурных переменных, которым при инициализации или по другим причинам было присвоено значение nil, совместимое с любым процедурным типом, приводит к ошибке. Для исключения подобных ошибок обычно используется проверка вида if @P <> nil then P(I, J).

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

tуре

MyProc =procedure (X,Y : Byte);

Ref= ^Node;

Node =record

K.Y : Byte;

IsVisible : Boolean;

DrawProc : MyProc;

Next : Ref

end;

NodeArray = array [1..5] of ref;

var

N : Node;

NA : NodeArray;

 . . .

Приведенное описание типов и переменных соответствует некоторой динамической структуре (линейному списку), ссылки на элементы которой “собраны” в массив NA. При таком описании возможны обращения вида:

 

N.DrawProc(1,5);

NA[4]^.DrawProc(6, 2);

и т. п., которые в определенной мере ограничивают присущую языку Паскаль ясность построения программ. Однако подобные обращения считаются одним из профессиональных достоинств языков, ориентированных на объектно-ориентированную технологию программирования (например С++), реализация которой  была бы невозможна без введения понятия процедурного типа.




1. географическое положение
2. Theories of European Integration
3.  Планирование помогает включиться в работу с первого дня пребывания в ВУЗе установить определенный порядок
4. Тема- Предмет и задачи педагогики
5. Курсовой проект по дисциплине- Электрическая часть электрических станций.html
6. тема; 3
7. Тема 44 Держава і право Англії нового часу План лекції 1
8. Контентанализ документов- сущность и специфика
9.  Миграции первобытных коллективов
10. реферат дисертацiї на здобуття наукового ступеня кандидата медичних наук