Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Delphi через Прат.doc
Скачиваний:
2
Добавлен:
14.11.2019
Размер:
631.81 Кб
Скачать

Volume: Integer;

price: Real;

end;

Ключевое слово record указывает на то, что код определяет компоновку записи. Идентификатор TInflatable — имя, или дескриптор, для этой формы. Таким образом, TInflatable представляет собой имя нового типа данных. Теперь можно создавать переменные типа TInflatable так же, как переменные типа char или integer. Между словами record и end; находится список типов данных, которые будут содержаться в структуре. Каждый элемент списка — это оператор объявления. Здесь можно использовать любой из типов данных, включая массивы и другие структуры. В этом примере применяется тип String для сохранения строки, а также Real и Integer. Каждый отдельный элемент в списке называется элементом структуры или полем записи, так что структура TInflatable включает три элемента.

Когда шаблон готов, можно создавать переменные данного типа:

var

hat, vincent: TInflatable; // структурные переменные типа TInflatable

Поскольку переменная hat имеет тип TInflatable, для обращения к отдельным элементам можно использовать оператор принадлежности (.). Например, выражение hat.volume указывает на элемент volume структуры, a hat.price — на элемент price. Аналогично vincent.price — элемент price переменной vincent. Итак, имена элементов предоставляют возможность обращения к элементам структуры во многом подобно тому, как индексы позволяют обращаться к элементам массива. Поскольку элемент price объявлен как тип Real, элементы hat.price и vincent.price эквивалентны переменным типа Real, и их можно использовать так же, как используется обыкновенная переменная типа Real. Таким образом, hat — это структура, но hat.price — элемент типа Real.

Программа 3_2

// простая запись

program Project1;

{$APPTYPE CONSOLE}

uses

SysUtils;

type

TInflatable = record

name: String;

volume: Real;

price: Real;

end;

var

Guest: TInflatable = (name: 'Glorious Gloria'; volume: 1.88; price: 29.99);

Pal: TInflatable = (name: 'Audacious Arthur'; volume: 3.12; price: 32.99);

r: Real;

begin

Writeln('Expand your guest list with ', Guest.name, ' and ', Pal.name);

r := Guest.price + Pal.price;

Writeln('You can have both for $', Format('%G', [Guest.price+Pal.price]),'!');

Readln;

end.

Результат:

Expand your guest list with Glorious Gloria and Audacious Arthur

You can have both for $62,98!

Другие свойства структур

Определяемые пользователем типы настолько близки по свойствам к встроенным типам, насколько это возможно. Например, можно передавать структуры функциям в качестве аргументов, а также использовать структуру, как возвращаемое значение функции. Кроме того, допускается использование оператора присвоения (=), чтобы присвоить значение одной структуры другой структуре того же типа. Такое действие приводит к тому, что каждому элементу одной структуры будет присвоено значение соответствующего элемента другой структуры, даже если этот элемент — массив. Данный вид операций называется поэлементным присваиванием.

Программа 3_3

// присвоение значений структурам

program Project1;

{$APPTYPE CONSOLE}

uses

SysUtils;

type

TInflatable = record

name: String;

volume: Real;

price: Real;

end;

var

Bouquet: TInflatable = (name: 'Sunflowers'; volume: 0.20; price: 12.49);

Choice: TInflatable;

begin

Writeln('Bouquet: ', Bouquet.name, ' for $', Bouquet.price:0:2, '!');

Choice := Bouquet; //присвоение значения одной структуры другой структуре

Writeln('Choice: ', Choice.name, ' for $', Choice.price:0:2, '!');

Readln;

end.

Результат:

Bouquet: Sunflowers for $12.49!

Choice: Sunflowers for $12.49!

Как видим, здесь выполнено поэлементное присваивание: элементам структуры choice присвоены те же самые значения, что хранились в структуре bouquet.

Массивы структур

Можно также создавать массивы, элементы которых будут структурами. Методика — точно такая же, как и при создании массивов базовых типов данных. В следующем примере создается массив из 100 структур TInflatable:

var

Gifts: array [1..100] of TInflatable;

В результате будет создан массив Gifts типа TInflatable. Следовательно, каждый элемент массива, такой как Gifts[1] или Gifts[100], — это объект типа TInflatable, и его можно использовать с оператором принадлежности:

WrietLn(Gifts[1].volume); //используется элемент volume первой структуры

WrietLn(Gifts[100].price); //отобразить элемент price последней структуры

Передача структур функциям

Программа 3_31

program Project1;

{$APPTYPE CONSOLE}

uses

SysUtils;

type

TPers = record

Fam: string;

Year: Integer;

end;

var

Pers: TPers;

procedure p(Zap: TPers);

begin

Writeln(Zap.Fam + ' ' + IntTostr(Zap.Year));

end;

begin

Pers.Fam := 'Ivanov';

Pers.Year := 1997;

p(Pers);

Readln;

end.

Зарезервированное слово with

Обычно для получения доступа к полям записи необходимо всегда вначале указывать имя записи. Но применение зарезервированного слова with позволяет указывать имя записи только один раз. Это зарезервированное слово служит для определения блока, в котором можно непосредственно обращаться к полям указанной записи.

Синтаксис записи блока with показан ниже:

with ИмяЗаписи do

begin

Поле_1 := значение;

Поле_2 := значение;

Поле_n := значение;

end;

Блок with позволяет уменьшить объем исходного кода, особенно при использовании записи с большим количеством полей или записи с действительно длинным именем.

with Book do

begin

Title := 'Bring Me the Head of Prince Charming';

Authors[1] := 'Roger Zelazny';

Authors[2] := 'Robert Sheckley';

PageCount := 280;

end;

Следующий пример иллюстрирует определение полей записи в другой записи и использование массивов записей.

program Project1;

{$APPTYPE CONSOLE}

uses

SysUtils;

type

TSex = (sxMale, sxFemale);

TAuthor = record

FirstName: string;

MiddleName: string;

LastName: string;

Age: Integer;

Sex: TSex;

end;

TBook = record

ISBN: string;

Title: string;

PageCount: Integer;

Authors: array[1..4] of TAuthor;

end;

var

TownLibrary: array[1..100] of TBook;

begin

with TownLibrary[1] do

begin

Title := 'StarShip Troopers';

with Authors[1] do

begin

FirstName := 'Robert';

MiddleName := 'A.';

LastName := 'Heinlein';

Sex := sxMale;

end;

end;

Readln;

end.

Параметры массивов и открытых массивов

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

procedure WriteStrings(Str: array[1..20] of string);

begin

end;

Если процедуре нужно передать массив, состоящий из 20 строк, потребуется создать новый тип данных, а затем создать процедуру, которая принимает новый тип данных в качестве параметра. Для создания нового типа данных необходимо использовать зарезервированное слово type.

Чтобы создать новый тип данных, который представляет массив из 20 строк, необходимо написать такой код:

program Project1;

{$APPTYPE CONSOLE}

uses

SysUtils;

type

TSmallArray = array[1..20] of string;

begin

end.

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

type

TSmallArray = array[1..20] of string;

procedure WriteStrings(StrArray: TSmallArray);

var

item: string;

begin

for item in StrArray do

Writeln(item);

end;

С этим решением связан один недостаток; процедура WriteString может принимать только переменную TSmallArray и, следовательно, мы ограничены массивом, состоящим из 20 строк. Если нужно работать с массивами других размеров, необходимо определить параметр типа открытого массива.

Синтаксис параметра открытого массива аналогичен синтаксису объявления динамического массива:

procedure ИмяПроцедуры(ИмяМассива: array of ТипДанных);

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

program Project1;

{$APPTYPE CONSOLE}

uses

SysUtils;

type

TSmallArray = array[1..20] of string;

var

SmallArray: TSmallArray;

DynamicArray: array of string;

StaticArray: array[1..100] of string;

procedure WriteStrings(StrArray: array of string);

var

item: string;

begin

for item in StrArray do

Writeln(item);

end;

begin

WriteStrings(SmallArray);

WriteStrings(DynamicArray);

WriteStrings(StaticArray);

Readln;

end.

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

procedure ИмяПроцедуры([выражение_1, выражение_2, выражение_n);

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

WriteStrings(['Open', 'Array', 'Constructor']);

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

program Project1;

{$APPTYPE CONSOLE}

uses

SysUtils, Math;

function Average(num1, num2: Integer): Integer; overload; inline;

begin

Result := (num1 + num2) div 2;

end;

{функции открытых массивов не могут быть помечены директивой inline}

function Average(nums: array of Integer): Integer; overload;

var

n: Integer;

begin

Result := 0;

for n in nums do

Inc(Result, n);

Result := Result div Length(nums);

end;

begin

Writeln(Average(2, 4));

Writeln(Average([56, 21, 33, 44, 59]));

Readln;

end.

Указатели и свободная память

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

• где хранится информация

• какое значение там хранится

• вид хранящейся информации

Для реализации этих свойств использовался один из возможных методов — определение простой переменной. Оператор объявления определяет тип и символическое имя значения, а также заставляет программу выделить область памяти для значения и внутренними средствами отслеживать ее адрес.

Теперь рассмотрим второй метод. Он приобретает особое значение при разработке классов. Этот метод основан на указателях, которые являются переменными, сохраняющими адреса значений вместо непосредственно самих значений. Но прежде чем рассматривать указатели, обратимся к способу явного определения адресов обычных переменных. Этот способ заключается в применении к переменной операции определения адреса, представленной знаком @. Например, если home — переменная, @home является ее адресом. Применение этого оператора продемонстрировано в листинге

Программа 3_4

// использование оператора @ для определения адреса

program Project1;

{$APPTYPE CONSOLE}

uses

SysUtils;

var

Donuts: Integer;

Cups: Real;

begin

Donuts := 6;

Cups := 4.5;

Write('Donuts value = ', Donuts);

Writeln(' and Donuts address = ', Integer(@Donuts));

Write('Cups value = ', Cups:1:1);

Writeln(' and Cups address = ', Integer(@Cups));

Readln;

end.

Результат:

Donuts value = 6 and Donuts address = 4252192

Cups value = 4.5 and Cups address = 4252196

При отображении адресов оператор WriteLn использует десятичное представление. В нашей реализации значение переменной donuts хранится в ячейке памяти с меньшей величиной адреса по сравнению с переменной cups. Разность между двумя адресами составляет 4. Это имеет смысл, поскольку переменная donuts имеет тип Integer, размерность которого составляет четыре байта. Конечно, в разных системах значения адресов будут различными. Кроме того, значение переменной cups может предшествовать в памяти значению переменной donuts. При этом разница адресов будет составлять 8 байтов, поскольку переменная cups имеет тип Real.

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

В соответствии с новой стратегией обработки хранящихся в памяти данных местоположение значения трактуется как именованная величина, а значение — как производная (порожденная) величина. Специальный тип переменной — указатель — содержит адрес значения. Таким образом, имя указателя представляет местонахождение значения в памяти. Применяя операцию ^, называемую косвенным значением или операцией разыменования, получаем значение, хранящееся по данному адресу. Предположим, что manly — это указатель. Тогда выражение manly представляет собой адрес, a manly^ — значение по данному адресу. Комбинация manly^ становится эквивалентом обычной переменной типа Integer. Эти моменты отражены в листинге 3_5. Здесь также показано, как объявлять указатель.

Программа 3_5

// наша первая переменная-указатель

program Project1;

{$APPTYPE CONSOLE}

uses

SysUtils;

var

updates: Integer; //объявление переменной

p_updates: ^Integer; //объявление указателя на значением типа Integer

begin

updates := 6;

p_updates := @updates; //присвоение адреса значения типа Integer указателю

//выражения значений двумя способами

Writeln('Values: updates = ', updates, ', p_updates^ = ', p_updates^);

//выражение адресов двумя способами

Write('Addresses: @updates = ', Integer(@updates));

Writeln(', p_updates = ', Integer(p_updates));

//использование указателя для изменения значения

p_updates^ := p_updates^ + 1;

Writeln('Now updates = ', updates);

Readln;

end.

Результат

Values: updates = 6, p_updates^ = 6

Addresses: @updates = 4252192, p_updates = 4252192

Now updates = 7

Очевидно, что переменная updates типа Integer и переменная-указатель p_updates являются всего лишь двумя сторонами одной и той же медали. Переменная updates представляет прежде всего значение, а операция @ используется для получения адреса, тогда как переменная p_updates представляет адрес, а операция ^ служит для получения значения. Поскольку p_updates указывает на значение updates, выражения p_updates^ и updates совершенно эквивалентны. Выражение p_updates^ можно использовать точно так же, как переменную типа Integer. Как видно из программы, можно даже присваивать значения переменной p_updates^. При этом изменяется указываемое значение переменной updates.

Объявление и инициализация указателей

Рассмотрим процесс объявления указателей. Компьютеру необходимо отслеживать тип значения, на которое ссылается указатель. Например, адрес переменной типа char выглядит так же, как и адрес переменной типа real, но для типов char и real используется различное количество байтов и различные внутренние форматы для хранения значений. Поэтому при объявлении указателя необходимо точно определять, на какой тип данных он указывает.

Последний пример содержит следующее объявление:

p_updates: ^Integer;

Это выражение указывает, что комбинация p_updates^ принадлежит к типу Integer. Поскольку операция ^ выполняется по отношению к указателю, переменная p_updates сама должна быть указателем. Мы говорим, что p_updates указывает на значение типа Integer. Мы также говорим, что типом для p_updates должен быть указатель на Integer. Повторим: p_updates является указателем (адресом), a p_updates^ — переменной типа Integer, но не указателем.

Для объявления указателей на другие типы переменных используется аналогичный синтаксис:

tax_ptr: ^Real; //tax_ptr указывает на тип Real

str: ^Char; //str указывает на тип Char

Поскольку tax_ptr объявляется как указатель на тип Real, компилятор определяет, что значение tax_ptr^ принадлежит к типу Real. Другими словами, он распознает, что tax_ptr^ представляет собой величину, которая хранится в формате с плавающей точкой и занимает (в большинстве систем) восемь байтов. Переменная-указатель никогда не бывает просто указателем. Она всегда указывает на определенный тип данных. Так, переменная tax_ptr имеет тип "указатель на тип Real", a str — "указатель на тип char". Несмотря на то, что обе переменные являются указателями, они указывают на два различных типа данных. Подобно массивам, указатели являются производными от других типов данных.

Заметим, что в то время, как tax_ptr и str указывают на типы данных, имеющие различную размерность, сами по себе две переменные — tax_ptr и str — имеют одинаковый размер. Иными словами, адрес переменной типа char имеет тот же размер, что и адрес переменной типа Real (точно так же, как номер дома 1016 может означать адрес большого универмага, а 1024 — адрес маленького коттеджа). По размеру или значению адреса ничего нельзя сказать о размере или типе переменной, так же, как и по номеру дома — о здании, которое находится по этому адресу. Обычно для хранения адреса требуются два или четыре байта, в зависимости от компьютера (в некоторых системах могут использоваться более длинные адреса или адреса различной длины для различных типов данных).

Для инициализации указателя можно использовать оператор объявления. В этом случае инициализируется указатель, а не указываемое значение. Таким образом, следующие операторы:

var

higgens: Integer = 5;

pt: ^Integer = @higgens;

присваивают указателю pt адрес переменной higgens.

Программа 3_6

// инициализация указателя

program Project1;

{$APPTYPE CONSOLE}

uses

SysUtils;

var

higgens: Integer = 5;

pi: ^Integer = @higgens;

begin

Writeln('Value of higgens = ', higgens, '; Address of higgens = ',

Integer(@higgens));

Writeln('Value of pi^ = ', pi^, '; Address of pi = ',

Integer(pi));

Readln;

end.

Результат:

Value of higgens = 5; Address of higgens = 4237700

Value of pi^ = 5; Address of pi = 4237700

Можно видеть, что программа присваивает адрес переменной higgens указателю pi, но не значению pi^.

Отметим однако, что неосторожное использование указателей может привести к нежелательным последствиям. Крайне важно не забывать о том, что при создании указателя компьютер выделяет память для хранения адреса, но не для хранения данных, на которые указывает этот адрес. Чтобы выделить места для данных, требуется дополнительное действие. Если вы такое действие не выполните (как в приведенном ниже примере), то окажетесь на прямом пути к краху:

var

fellow: ^Integer; //создание указателя на переменную типа Integer

begin

fellow^ := 5; //помещение значения по неопределенному адресу

Конечно, переменная fellow является указателем. Но на что она указывает? Программа не присвоила ей адрес. Куда же будет помещено значение 5? Мы не в состоянии ответить на этот вопрос. Поскольку переменная fellow не была инициализирована, она может принять любое значение. Какое бы значение она ни имела, программа будет интерпретировать его как адрес, по которому нужно сохранить число 5. Если fellow имеет значение 1200, то компьютер попытается поместить данные по адресу 1200, даже если окажется, что этот адрес находится в середине кода программы. Понятно одно: куда бы ни указывал указатель fellow, это не то место, куда вы намерены поместить число 5. Ошибки подобного рода коварны. Кроме того, их сложно выявлять.

Золотое правило для указателей: ВСЕГДА присваивайте указателю определенное и правильное значение адреса до применения к нему операции разыменования (^).

Выделение памяти с помощью оператора New

Теперь, когда вы имеете некоторое представление о том, как работают указатели, давайте посмотрим, как они могут реализовать такую важную технологию ООП, как распределение памяти во время выполнения программы. До сих пор мы присваивали указателям адреса переменных. Переменная является именованной областью памяти, которая резервируется во время компиляции, а указатели всего лишь служат псевдонимами для областей памяти, к которым можно в любом случае обратиться напрямую по имени. Истинная ценность указателей проявляется тогда, когда для хранения данных выделяется неименованная область памяти во время выполнения программы. В этом случае указатели становятся единственным способом доступа к этим областям памяти. Для этого существует оператор new.

Попробуем использовать эту новую технологию — создадим неименованное хранилище значений типа Integer во время исполнения программы и будем обращаться к этим значениям с помощью указателя. Здесь ключевое значение имеет оператор new. Мы указываем оператору new, для какого типа данных выделяется память. Оператор new ищет блок памяти нужного размера и возвращает адрес этого блока. Присваивая этот адрес указателю, мы получаем желаемое. Ниже приводится пример реализации описанной технологии:

var

pi: ^Integer;

begin

New(pi);

Выражение New(pi) сообщает программе о том, что необходимо некоторое новое хранилище, подходящее для размещения значения типа Integer. Оператор new анализирует тип для вычисления необходимого количества байтов. Затем он отыскивает область памяти и возвращает адрес. Далее присваиваем адрес переменной pi, которая объявляется как указатель на тип Integer. Теперь рi является адресом, а рi^ — значением, хранящимся по этому адресу. Сравним это с присваиванием указателю адреса переменной:

var

pt: ^Integer;

higgens: Integer;

begin

pi := @higgens;

В обоих примерах (рi и pt) указателю присваивается адрес переменной типа Integer. Во втором случае можно также обращаться к переменной типа Integer по имени: higgens. В первом же случае обращение возможно исключительно через указатель. Возникает вопрос: так как область памяти, на которую указывает рi, не имеет имени, как же к ней обращаться? Мы говорим, что рi указывает на объект данных. Но это не "объект" в смысле "объектно-ориентированного программирования"; это всего лишь "объект" в смысле "предмет". "Объект данных", под которым понимается любой блок памяти, выделенный для хранения элементарной группы данных, является более общим понятием, чем "переменная". Поэтому переменная также является объектом данных, но память, на которую указывает рi, не является переменной. Способ управления объектами данных с помощью указателей может показаться на первый взгляд довольно неудобным, однако он обеспечивает большие возможности по управлению организацией памяти в программе.

Программа 3_7

// использование оператора new

program Project1;

{$APPTYPE CONSOLE}

uses

SysUtils;

var

pt: ^Integer;

pd: ^Real;

begin

New(pt); //выделение области памяти для значений типа Integer

pt^ := 1001; //место хранения значения

Writeln('Integer value = ', pt^, ': location = ', Integer(pt));

New(pd); //выделение области памяти для Real

pd^ := 10000001.0; //место хранения значения типа Real

Writeln('Real value = ', pd^:1:1, ': location = ', Integer(pd));

Write('size of pt = ', SizeOf(pt));

Writeln(': size of pt^ = ', SizeOf(pt^));

Write('size of pd = ', SizeOf(pd));

Writeln(': size of pd^ = ', SizeOf(pd^));

Dispose(pt); //освобождаем память

Dispose(pd);

Readln;

end.

Результат:

Integer value = 1001: location = 10571360

Real value = 10000001.0: location = 10571376

size of pt = 4: size of pt^ = 4

size of pd = 4: size of pd^ = 8

Примечания к программе

Программа использует оператор new для выделения памяти объектам данных типа Integer и Real. Это происходит при выполнении программы. Указатели pt и pd указывают на эти два объекта данных. Без них невозможно обращаться к выделенным участкам памяти. Благодаря указателям, можно использовать выражения pt^ и pd^ просто как переменные. Значения выражениям pt^ и pd^, присваиваются для того, чтобы присвоить значения новым объектам данных. Аналогично, выражения pt^ и pd^ используются для отображения этих значений с помощью оператора WriteLn.

Программа также демонстрирует одну из причин, по которой необходимо объявлять тип данных, на которые указывает указатель. Адрес сам по себе определяет только начало области памяти, в которой хранится объект, но не его тип или занимаемый размер. Взгляните на адреса двух значений из нашего примера. Они — всего лишь числа без обозначения типа или размера. К тому же заметим, что размерность указателя на тип Integer совпадает с размерностью указателя на тип Real: оба являются просто адресами. Но поскольку в программе объявлены типы указателей, программа знает, что значение pd^ относится к типу Real с размерностью восемь байтов, в то время как значение pt^ относится к типу Integer размерностью четыре байта. Когда программа выводит значение pd^, оператор WriteLn определяет, сколько байтов нужно прочесть и как их представить.

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

Использование оператора new для запроса памяти представляет собой наиболее эффектную сторону механизма управления памятью. Другой стороной является оператор dispose, который позволяет высвободить память ("вернуть" в общую область) после того, как ее использование завершено. Это — важный шаг к наиболее эффективному использованию памяти. Память, которая возвращается, или высвобождается, может быть повторно использована другими модулями программы.

var

ps: ^Integer;

begin

New(ps); //выделение области памяти для значений типа Integer

...

Dispose(ps); //освобождаем память

В этом примере очищается область памяти, на которую указывает ps, но сам указатель ps не удаляется. Его можно использовать повторно, например, для указания на другую выделенную область памяти. Операторы new и dispose всегда необходимо применять сбалансирование, иначе может произойти утечка памяти; другими словами, наступит момент, когда выделенную память нельзя будет больше использовать. Если утечка памяти чересчур велика, она может привести к останову программы при поиске дополнительной памяти.

Не следует пытаться повторно очистить блок памяти, который уже высвобожден. Результат такого действия не определен. Нельзя также использовать оператор dispose для освобождения памяти, созданной при объявлении переменных:

var

ps: ^Integer;

pi: ^Integer;

jugs: Integer;

begin

New(ps); //допустимо

Dispose(ps); //допустимо

Dispose(ps); //уже недопустимо

jugs := 5; //допустимо

pi := @jugs; //допустимо

Dispose(pi); //недопустимо, память не выделена оператором new

Отметим, что важным залогом корректности работы оператора dispose является то, что он должен применяться по отношению к области памяти, выделенной с помощью оператора new. Это не значит, что нужно использовать тот же указатель, что и для new; важно, чтобы это был тот же самый адрес:

var

ps, pq: ^Integer;

begin

New(ps); //распределение памяти

pq := ps; //второму указателю присваивается адрес того же самого блока памяти

Dispose(pq); //применение оператора dispose со вторым указателем

Использование оператора New для создания динамических структур

Если требуется выделять область памяти только для такого количества структур, которое необходимо программе в определенный момент, снова приходит на помощь оператор New. Он позволяет создавать динамические структуры. Опять же, слово "динамические" означает то, что память выделяется во время выполнения программы, а не во время компиляции. Кроме того, рассмотренная выше техника работы со структурами применима к классам, так как классы во многом похожи на структуры.

Использование оператора New при работе со структурами происходит в два этапа: создание структуры и доступ к ее элементам. Например, создать безымянную структуру типа TInflatable и присвоить ее адрес подходящему указателю можно следующим образом:

var

ps: ^TInflatable;

begin

New(ps);

Здесь указателю ps присваивается адрес части свободной памяти, достаточной для хранения структуры типа TInflatable.

Получить доступ к элементам структуры сложнее. Например, если ps указывает на структуру типа TInflatable, то ps^.price — это элемент price указанной структуры.

Программа 3_8

// использование оператора new для создания структуры

program Project1;

{$APPTYPE CONSOLE}

uses

SysUtils;

type

TInflatable = record //шаблон структуры

name: String[20];

volume: Real;

price: Real;

end;

var

ps: ^TInflatable;

begin

New(ps); //выделение области памяти для структуры

Write('Enter name of inflatable item: ');

Readln(ps^.name);

Write('Enter volume in cubic feet: ');

Readln(ps^.volume);

Write('Enter price: $');

Readln(ps^.price);

Writeln('Name: ', ps^.name);

Writeln('Volume: ', ps^.volume:1:2);

Writeln('Price: ', ps^.price:1:2);

Dispose(ps);

Readln;

end.

Результат:

Enter name of inflatable item: Fabulous Frodo

Enter volume in cubic feet: 1.4

Enter price: $17.99

Name: Fabulous Frodo

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]