В.Г. Абрамов, Н.П. Трифонов, Г.Н. Трифонова - Введение в язык Паскаль (1107618), страница 56
Текст из файла (страница 56)
Таким образом, описаниеvar v:+Тлишь вводит в употребление статическую переменную v ссылочного типа(по этому описанию транслятор только отводит место в памяти, необходимое для размещения ссылки), но не вводит в употребление никакого программного объекта типа Т, на который будет указывать значение этойссылочной переменной v. Это описание говорит лишь о том, что значениямипеременной v могут быть ссылки на такие объекты. Для порождения жесамого динамического объекта служит стандартная процедура с именем new (новый) , обращение к которой производится с помощью оператора процедуры.
В этом операторе процедуры задается один фактическийпараметр — ссылочная переменная, сопоставленная порождаемомудинамическому объекту, например (имея в виду приведенные вышеописания) n e w ( p ) . В результате выполнения оператора процедуры такоговида порождается новый объект типа, указанного в описании той ссылочнойпеременной, которая задана в качестве фактического параметра, и в качестве значения этой ссылочной переменной присваивается ссылка на этотвновь порожденный объект (с точки зрения машинной интерпретации этоозначает, что в памяти резервируется место для порождаемого объекта,а адрес начала этого места присваивается заданной ссылочной переменной).В данном случае порождается переменная типа integer, а указателю р в качестве значения присваивается ссылка на эту только что порожденнуюпеременную.Обратим внимание на то, что при этом порожденному динамическомуобъекту не присваивается какого-либо значения, так что для динамического239объекта процедура new играет ту же роль, что и описание для статическогообъекта.Теперь рассмотрим вопрос о том, как работать с динамическим объектом, т.е.
как присваивать ему то или иное значение и как использовать этозначение. Здесь основной вопрос состоит в том, как же в паскаль-нрограмме ссылаться на динамический объект? Как видно из сказанного выше,динамическим объектам, в отличие от статических, не дается имен в обычном понимании этого слова.
Поэтому для ссылки на динамический объектв языке имеется такое понятие, как переменная с указателем :< переменная с указателем > ::= < ссылочная переменная > tВ простейшем случае (ссылочная переменная > есть имя той статическойпеременной ссылочного типа, которая в программе поставлена в соответствие данному динамическому объекту. Стрелка t после ссылочной переменной свидетельствует о том, что здесь речь идет не о значении самой ссылочной переменной, а о значении того программного объекта, на которыйуказывает эта ссылочная переменная.Так, если в программе имеется описание переменной р:varр:tinteger;то при выполнении оператора процедуры new(p) порождается динамическая переменная типа integer; если затем будет выполнен оператор присваиванияр t:=58то упомянутой выше динамической переменной будет присвоено значение,равное 58.Переменная с указателем (а именно она синтаксически и выполняет рольдинамической переменной) может быть использована в любых конструкциях языка, где допустимо использование переменных того типа,что и тип динамической переменной.
Так, если г — также переменная типа integer, то допустимы операторы присваиванияr:=r+pt+2; р+:=р+ div 3; рабмасСр++13:=99;В качестве ссылочной переменной может использоваться и более сложнаяконструкция, являющаяся частичной переменной, которая имеет соответствующий ссылочный тип. Так, если в программе имеется описание ссылочного типа:refreal=trealи описание переменной:ft: array CI.. 503 o-f re-freal(в силу которого значением переменной А может быть последовательностьзначений ссылочного типа, причем каждая из этих ссылок указываетнавещественное значение), то в качестве ссылочной переменной может фигурировать переменная с индексом, например А [2] или А [к + 5], а соответствующие им переменные с указателем будут выглядеть следующим образом: А [2] t и А [к + 5] t — значениями этих переменных с указателем будутуже вещественные числа.24013.2.
Действия над ссылкамиИтак, мы ввели понятие ссылки и ссылочного типа значений, а также показали, как в программе задавать действия над значениями динамическихобъектов, используя для этой цели соответствующие им статические переменные ссылочного типа. Теперь рассмотрим операции над значениямиссылочного типа.Прежде всего заметим, что над значениями ссылочного типа в паскаленет каких-либо операций, которые бы давали результат этого же типа.И это понятно — ведь значения ссылочного типа в конечном счете являютсяадресами тех или иных программных объектов в памяти машины.А поскольку язык не предусматривает каких-либо правил размещенияэтих объектов в памяти машины (это размещение производится транслятором по своему усмотрению), то невозможно сформулировать какие-либоразумные правила (операции), с помощью которых в программе можнобыло бы определить адрес одного объекта по адресам других объектов.Так что над значениями ссылочного типа определены только операция присваивания и некоторые операции сравнения.Присваивание.
Для присваивания значения ссылочной переменной v, какобычно, используется оператор присваиванияv: = егде е — ссылочное выражение, которое задает ссылочное значение того жетипа, что и переменная v.При этом в качестве ссылочного выражения может использоваться:— пустая ссылка nil;— ссылочная переменная;— ссылочная функция ( т .
е. функция, значением которой являетсяссылка).Следует подчеркнуть, что переменная или функция, являющиеся ссылочным выражением е в операторе присваивания, должны иметь тот же самыйтип, что и переменная v, т.е. значения v и е должны ссылаться на программные объекты одного и того же типа. Так что, например, при наличиив программе приведенных выше описаний ссылочных переменных р и qоператор присваиванияp:=qнедопустим, поскольку — в соответствии с описанием — переменная р может ссылаться на значение типа integer, а переменная q — на значениетипа char.Для пояснения результатов присваивания значений ссылочным переменным и переменным с указателем рассмотрим следующий пример.Пусть в программе имется описание ссылочных переменных:p,d:tintegerПроследим изменения значений этих ссылочных переменных и соответствующих переменных с указателями в результате последовательного выполнения операторов присваиванияр + :=3; d t:=58; p:=d; d:=nil;16.
В.Г. Абрамов241C p t » - 3 i df: = 58) p(p»-di)p(d-' = n i l ; )p* f T ldCH3ШCD dDDH~58~ld p g - ^ f i r |LQS]Рис. 1.3.1. Изменение значений указателейДля наглядности получаемые при этом результаты представим в видесхем на рис. 13.1.Обратите внимание, что в данном примере после выполнения оператораприсваивания р : = d на динамический объект со значением 3 не указываетни одна ссылка, т.е.
он стал недоступным для программы. С другой стороны, в результате выполнения этого же оператора присваивания не образуется какой-либо новый динамический объект со значением 58, а переменная рбудет ссылаться на тот же уже существующий динамический объект, на который ссылается и переменная d.Важно четко понимать разницу между ссылкой, т.е. значением ссылочнойпеременной, и значением динамического объекта, т.е. значением соответствующей переменной с указателем. Рассмотрим возможные комбинацииоператоров присваивания, в которых участвуют ссылочные переменные ри d, определенные выше, и соответствующие переменные с указателем ptи d t , а самое главное, проследим, как изменяются соответствующиессылки и значения динамических объектов.
Пусть аналогично предыдущемупримеру выполняются операторы p t : = 3 ; d t : =58. В результате будемиметь:СБ-58Значения ссылочных переменных и соответствующих переменных с указателями, полученные в результате последующего выполнения оператора р: = d, можно изобразить схемойИ'В24258Если же вместо оператора р : =d выполнить оператор p t : = d t , то получимследующий результат:Приведем примеры и неправильного использования ссылочных переменных и переменных с указателем в операторах присваивания (учитываяранее приведенные описания):р: = d t (в левой части оператора присваивания фигурирует переменнаяссылочного типа, а в правой части — переменная с указателем типа integer);p t : = ' a ' (в левой части — переменная с указателем типа integer, а вправой части — литерная константа ' а ' ) ;p t : = nil (в левой части — переменная с указателем типа integer, а в правой части — значение ссылочного типа).Итак, использование динамических переменных имеет следующие отличия от использования статических переменных:— вместо описания самих динамических переменных в программе даютсяописания указателей (статических переменных ссылочного типа), поставленных в соответствие динамическим переменным;— в подходящем месте программы должно быть предусмотрено порождение каждой из динамических переменных с помощью процедуры new;— для ссылки на значение динамической переменной используется переменная с указателем.Что касается манипулирования значениями переменных с указателем, тоздесь никаких особенностей не возникает, так как к ним можно применятьвсе те операции, которые допустимы и для значений статических переменных этого же типа.Заметим, что в паскале нет каких-либо правил относительно того, какиепеременные должны быть статическими, а какие динамическими.
В принципе вместо любой статической переменной можно использовать динамическую переменную. Однако следует иметь в виду, что замена статическойпеременной на динамическую удлиняет текст программы (за счет операторапроцедуры new), несколько снижает ее наглядность (за счет необходимости использования переменной с указателем) и снижает быстродействиепрограммы (за счет необходимости во время выполнения программы размещать порожденную переменную в памяти машины и формировать значение соответствующего указателя, а также за счет усложнения доступак значению динамической переменной).Пример13.1. Задан текст, состоящий из слов, разделенных запятыми, и оканчивающийся точкой, а каждое слово есть последовательностьбукв.
Предполагается, что любое слово текста содержит не более 100 букв.Требуется подсчитать число вхождений заданной буквы в первое по порядку самое длинное слово.Эту задачу естественно разбить на две подзадачи:1) найти первое по порядку слово максимальной длины;2) подсчитать число вхождений заданной буквы в найденное слово.16*243Первая из этих подзадач, которая и представляет для нас интерес, можетбыть решена по следующей обшей схеме (искомый результат здесь обозначается через Резслово):max: =-1;while не учтено последнее слово текста dobeginТекслово:= очередное слово;Длина:= число букв в текслово;i-f длина > max thenbeginРезслово:= Текслово;max:=длинаendendЭту схему можно реализовать в виде паскаль-программы разными способами.