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

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

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

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

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

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

от 25%

Подписываем

договор

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

Скидка 25% при заказе до 27.4.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го класу Мета роботи- Придбання студентами практичних навиків в проектуванні
2. 012006 г доходная часть местных бюджетов формировалась за счет собственных доходов поступлений от федеральн
3. тематическое получение прибыли от пользования имуществом продажи товаров выполнения работ или оказания ус
4. Сочетанная травма как основное заболевание
5. К югу от границы на запад от солнца Харуки Мураками К югу от границы на запад от солнца
6. 2014 рік ВІДОМОСТІ про вчителів методичного об~єднання вчителів тру
7. Определение сварочного поста
8. Эмоциональное развитие детей младшего дошкольного возраста
9. Двигательная активность и здоровье подрастающего поколения
10. Курсовой проект по дисциплине ФУОСП