
- •Утверждаю Зам.Директора по учебной работе
- •Основы Алгоритмизации и программирования учебно-методическое пособие
- •220301 Автоматизированные системы обработки информации и правления
- •Введение
- •Основные этапы решения задач на эвм
- •Глава 1 способы записи алгоритма
- •1.1 Алгоритм и его свойства
- •Схемы алгоритма
- •1.2 Структуры алгоритмов
- •1.2.1 Алгоритм линейной структуры
- •1.2.2 Алгоритм разветвляющейся структуры
- •1.2.3 Алгоритм циклической структуры
- •1.2.4 Алгоритм со структурой итерационных циклов
- •1.2.5 Алгоритм со структурой вложенных циклов
- •Глава 2 программа на языке высокого уровня
- •2.1 Системы программирования
- •2.2 Характеристика языка программирования Паскаль
- •2.3 Алфавит и структура программы на Паскале Алфавит программы
- •Структура программы
- •Глава 3 Стандартные типы данных
- •3.1 Данные. Типы
- •3.2 Вещественные типы
- •3.3 Целочисленные типы
- •3.4 Символьный тип
- •3.5 Логический тип
- •4 Представление основных структур программирования: итерация, ветвление, повторение
- •4.1 Линейная структура (следование)
- •Var X,y,f: real;
- •4.2 Разветвляющая структура (ветвление)
- •4.3 Циклическая структура (повторение)
- •4.3.1 Оператор цикла с параметром
- •I : Integrer; {номер числа }
- •4.3.2 Оператор цикла с постусловием
- •I,n: integer;
- •4.3.3 Оператор цикла с предусловием
- •4.3.4 Итерационные циклы
- •Var r,a:real;
- •Приближенное вычисление функций
- •Решение уравнений приближенными методами
- •Метод деления отрезка пополам
- •Xsl, Xpr, a, b, e, y1, y2, Lev, Prav, y: Real;
- •Метод Ньютона
- •Метод прохождения отрезка с переменным шагом
- •Вычисление определенных интегралов
- •1. Метод прямоугольников
- •X: Real;
- •2. Метод трапеций
- •X: Real;
- •Глава 5 Типы данных, определяемые пользователем
- •5.1 Пользовательский тип данных
- •5.1.1 Типизированные константы
- •5.1.2 Перечисляемый тип
- •I:1..6; loto: num;
- •5.2 Массивы
- •I : integer;
- •5.2.1. Работа с одномерными массивами
- •I,sum : integer;
- •Var a: array [1..N] of real;
- •Var I,s,r: integer;
- •I : list;
- •I : integer;
- •X : mass;
- •I, j, p, n, m, k:integer;
- •I, j, k, nd : integer;
- •Xmin : real;
- •X : mass;
- •Var I, j, nd : integer;
- •X : mass;
- •5.2.2 Работа с двумерными массивами( матицы)
- •Var I,j,n : integer;
- •I,j,n,m : integer;
- •5.2.3 Сортировка массивов
- •Сортировка методом "пузырька"
- •X : Array [1..Nmax] Of Real;
- •X : Array [1..Nmax] Of Real;
- •Сортировка выбором
- •Обменная сортировка
- •Var m:array[1..1000] of integer;
- •I,z,n:integer; Key:byte;
- •Сортировка слиянием
- •Var { Описание массивов и переменных}
- •X, y: array[1..1000] of integer;
- •5.3 Строковые типы
- •Var s: string[10];
- •5.3.1 Операции над строками
- •5.3.2 Стандартные процедуры и функции для строк
- •Функция Length
- •Функция Upcase
- •Функция Copy
- •Функция Роs
- •I, n, p: integer;
- •I: integer;
- •I: integer;
- •Insert (word2, text, I);
- •Insert (chr (k-32), t, I);
- •Insert (chr (k-80), t, I);
- •Insert (‘е’, t, I);
- •Глава 6 Процедуры и функции
- •6.1 Процедуры
- •I : Integer;
- •I, n: integer;
- •Input _ mas (k, n);
- •I,n : Integer;
- •I,k : Integer;
- •6.2 Функции
- •I:Integer;
- •2) Массивы;
- •I,n : Integer;
- •I : Integer;
- •I,tn,tk:Real;
- •Глава 7 Программирование рекурсивных алгоритмов
- •7.1 Понятие рекурсии
- •7.2 Техника построения рекурсивных алгоритмов
- •7.3 Формы рекурсий
- •If Prim(I) then
- •7.4 Рекурсия и итерация
- •7.5 Программирование с использованием рекурсии
- •Var p: Integer;
- •Var X, y: Integer; begin
- •Var z: Real; begin
- •Var I:integer; j:real;
- •Глава 8 Файлы
- •8.1 Текстовые файлы
- •I,n : Integer; {Вспомогательные переменные}
- •8.2 Типизированные файлы
- •X,m,s : Real;
- •8.3 Нетипизированные файлы
- •Глава 9 Записи
- •9.1 Описание записи
- •I: integer;
- •9.2 Оператор присоединения
- •I, j, k, m : integer;
- •X: real;
- •9.3 Вложенные записи
- •9.4 Записи с вариантами
- •Information: record
- •I, k, n : integer;
- •Vedom : Array [1..Nmax] Of Stud;
- •I,j : Integer;
- •Vedom : File Of Stud;
- •Vedom : File Of Stud;
- •I,j,kdv,k2 : Integer;
- •If Not Eof (Ftel) Then
- •If Not Eof(Ftel) then
- •If Not Eof(FilComp) then
- •Глава 10 Динамические структуры данных
- •10.1 Распределение памяти при выполнении программ
- •Верхняя граница памяти ms-dos
- •10.2 Ссылочные переменные
- •10.3 Процедуры управления кучей
- •10.4 Использование переменных ссылочного типа
- •I: Integer;
- •I, k : Integer;
- •Val(b, k, code);{Превратили второй символ в ч исло}
- •10.5 Списки
- •Var Ch : Char;
- •Var Ch : Char;
- •10.6 Деревья
- •10.7 Константы ссылочного типа
- •Глава 11. Язык Паскаль. Графический модуль Graph Список используемой литературы Основная
- •Дополнительная
10.2 Ссылочные переменные
Описание ссылочного типа выглядит следующим образом:
Туре Ptr = ^t,
где t - стандартный или заранее описанный тип данных, называемый базовым типом. Сами адреса будут храниться в ссылочных переменных, которые описываются обычным образом, например, Var Р : Ptr. Такие переменные для хранения адресов динамической памяти называются ссылками или указателями.
Например:
Туре
Pint = ^Integer;
W = array [1..20] of Real;
p = ^W;
Var
N : Pint;
U : p;
Под переменную N и переменную U будет отведено по 4 байта памяти в сегменте данных. Переменные будут содержать адрес какой-либо ячейки памяти, расположенной в динамической области. Но прежде, чем переменная ссылочного типа примет значение, необходимо в ходе выполнения программы выполнить специальную процедуру. Ссылка представляет собой адрес начала, т.е. первой ячейки, некоторого места в памяти, выделенного для объекта базового типа. Переменная U будет содержать адрес первой ячейки, выделенной под массив W в динамической области памяти.
В объявлениях ссылочных типов после символа "^" может стоять только простое имя типа. В случае сложных имен используется переопределение типов, как в приведенном примере.
Указатели, связанные с адресами значений конкретных базовых типов, называются типизированными. N и U - типизированные указатели. В Турбо Паскале можно объявлять указатель и не связывать его при этом с каким-либо конкретным типом данных. Такие указатели называются нетипизиро-ванными. Описание нетипизированных указателей осуществляется с помощью служебного слова Pointer, например, Var P : Pointer.
Адрес хранится как два слова: одно из них определяет сегмент, а другое - смещение. Значение указателя не может быть в явном виде выведено на экран или печать. Его надо предварительно расшифровать. Для работы с указателями вводится специальный набор функций:
Addr(X) |
Ссылка на начало объекта X в памяти (тип Pointer) Аналогом этой функции является операция @Х |
Seg(X) |
Сегмент, в котором хранится объект X (тип Word) |
Ofs(X) |
Смещение в сегменте для объекта X (тип Word) |
Ptr(S,O:Word) Pointer |
Ссылка на место в памяти, заданное значениями смещения О и сегмента S (тип Pointer) |
SizeOf(x) |
Размер объекта X в байтах |
Так как значение указателя состоит из двух слов (Word), хранящих сегмент и смещение, можно вывести их в отдельности, используя функции Seg и Ofs:
Writeln('Сегмент ', Seg(p), ' смещение ', Ofs(p));
Указатели могут обмениваться значениями через оператор присваивания. Типизированному указателю можно присвоить значение либо указателя того же типа, что и он сам, либо нетипизированного указателя. Если, например,
Var
Р1, Р2 : ^Integer;
РЗ : ^Real;
РР : Pointer;
то присваивание Р1 := Р2 вполне допустимо, в то время как Р1 := РЗ запрещено, поскольку Р1 и РЗ указывают на разные типы данных. Это ограничение не распространяется на нетипизированные указатели. Можно записать РР := РЗ; Р1 := РР и достичь нужного результата. Присутствие в программе таких переприсвоений говорит о том, что программист делает это осознано и в программе действительно нужны такие действия. Указателю можно присвоить значение Nil. Nil - это предопределенная константа типа Pointer, соответствующая адресу 0000:0000 (пустая ссылка). Если указателю присвоено значение Nil, то этот указатель ни на какие данные не ссылается.
Указатели могут сравниваться с помощью операций отношения = или <> (не равно). Сравнение для указателей - ненадежная операция. Если два указателя содержат один и тот же адрес в памяти, но записанный в них разными способами, то они считаются различными. Зато можно проверить, ссылается ли указатель р на что-нибудь или нет путем сравнения р <> Nil.
Содержимое ячейки доступно через имя указателя. Чтобы обратиться к данным, находящимся по адресу, содержащемуся в указателе, используется символ "^", который ставится сразу после имени ссылочной переменной. Эта операция называется операцией разыменования. Суть ее состоит в переходе от ссылочной переменной к значению, на которое она указывает. Пусть имеется следующее описание:
Var
a, b: ^Real;
тогда в программе с переменными а^ и b^ допустимы все действия, что и с любыми переменными типа Real, например,
а^ := b^;
b^ := sin(a"); и т.д.
Память под любую динамически размещаемую переменную выделяется процедурой New(p). Только после выполнения процедуры New имеет смысл обращаться к ссылочным переменным. Параметром обращения к этой процедуре является типизированный указатель. В результате обращения указатель приобретает значение, соответствующее адресу, начиная с которого, можно разместить данные. Это адрес динамической области данных или кучи. Начало кучи хранится в стандартной переменной Heaporg, конец - в переменной Heapend. Текущую границу незанятой динамической памяти указывает указатель Heapptr. Если Var I^ integer, то после выполнения New(i) указатель i приобретет значение, которое перед этим имел указатель кучи Heapptr, а сам Heapptr увеличит свое значение на 2, так как длина внутреннего представления типа Integer, с которым связан указатель i, составляет 2 байта. Надо понимать, что на самом деле механизм выделения памяти сложнее, но для нас важно понять принципиальную схему (рис. 2).
Рисунок 10.2 - Расположение кучи в памяти ПК
Динамическую память можно не только забирать из кучи, но и возвращать обратно. Для этого используется процедура Dispose. Dispose(i) для предыдущего примера вернет в кучу 2 байта. Процедура Dispose не изменяет значение указателя, а лишь возвращает в кучу память, ранее связанную с этим указателем.
Проанализируем результаты вывода в следующей программе:
Var
p1, ip : ^integer;
Begin
{1}Writeln(ip^,' ',p1^);
{Выводятся значения, размещенные в динамической памяти по адресу Heapptr)
new(ip); new(p1);
{2}Writeln(ip^,' ',p1^);
{Выводятся значения, размещенные в динамической памяти по адресу Heapptr}
ip^ := 7;
р1^ := 20;
{3}Writeln(ip^,' ',p1^); {7 20}
ip := р1;
{4}Writeln(ip^,' ',p1^); {20 20}
dispose(ip);
{dispose(p1);}
{Использовать нельзя, так как р1 и ip указывают на одну и ту же ячейку памяти}
{5}Writeln(ip^,' ',p1^); {20 20}
ip := nil;
{6}Writeln(ip^,' ',p1^); {Случайное значение и 20}
End.
Несмотря на то, что первый оператор вывода предшествует процедуре New, ошибки не произойдет. Однако за указателями ip и р1 ячейки еще не закреплены и могут быть использованы другими динамическими переменными. Первый оператор является некорректным: работать с динамическими переменными можно только после выполнения процедуры New. Второй оператор Writeln выведет ту информацию, которая на момент начала выполнения программы расположена в ячейках с адресом Heapptr и Heapptr + 2. Следующим оператором в эти ячейки будет записана информация: 7 и 20 соответственно. Третий оператор вывода выдаст эти значения. Значение указателя ip изменилось после выполнения присваивания ip := р1. Теперь оба указателя указывают на одну и ту же ячейку памяти, в которой хранится ,, число 20. К числу 7 нет доступа. Это значение теперь пассивно занимает , память, т.е. 7 превратилось в мусор. Освободить память можно только для одного указателя ip или р1, так как оба эти указателя указывают на одну и ту < же ячейку памяти. Пятый оператор Writeln выведет те же значения 20 и 20, но это лучше рассматривать как случайность. После выполнения процедуры Dispose(ip) значения ссылок ip и р1 не определено, как и значения ip" и р1А. Выполнение шестого оператора Writeln также не приведет к ошибке, хотя его присутствие не имеет никакого смысла. Приведем еще один пример.
Type
Plnteger = ^Integer;
Var
SomeNumber: Integer;
Begin
SomeNumber := 17;{присвоить SomeNumber 17}
SomeAddress := @SomeNumber; {SomeAddress указывает на SomeNumber}
Writeln(SomeNumber); {напечатать 17}
{Writeln(SomeAddress); не допускается; указатели печатать нельзя}
Writeln(SomeAddress^); {напечатать 17}
AnotherAddress := SomeAddress;{также указывает на SomeNumber}
AnotehrAddress^ := 99; {новое значение для SomeNumber}
Writeln(SomeNumber);{Напечатать 99} End.
Перед использованием указателей им всегда нужно присваивать значения. Если разыменовывается указатель, которому еще не присвоено значение, то считанные из него данные могут представлять собой случайные биты, а присваивание значения указываемому элементу может затереть другие данные, программу или даже операционную систему. Чтобы избежать разыменования указателей, которые не указывают на что-либо значащее, нужен некоторый способ информирования о том, что указатель недопустим. В Паскале предусмотрено зарезервированное слово Nil, которое можно использовать в качестве содержательного значения указателей, которые в данный момент ни на что не указывают. Указатель Nil является допустимым, но ни с чем не связанным. Перед разыменованием указателя нужно убедиться, что он отличен от Nil (не пуст).