Скачать презентацию Тема 9 Динамические структуры данных Часть 1 Скачать презентацию Тема 9 Динамические структуры данных Часть 1

Тема 9 Часть 1.ppt

  • Количество слайдов: 111

Тема 9. Динамические структуры данных Часть 1 © С. В. Кухта, 2010 Тема 9. Динамические структуры данных Часть 1 © С. В. Кухта, 2010

2 Содержание 1. 2. 3. 4. 5. Адресация оперативной памяти MS DOS Указатели и 2 Содержание 1. 2. 3. 4. 5. Адресация оперативной памяти MS DOS Указатели и операции над ними Управление динамической памятью Работа с динамическими массивами Динамические структуры данных: списки, стеки, очереди, деки 6. Деревья и графы 7. Примеры решения задач обработки динамических структур данных © С. В. Кухта, 2010

3 1. Адресация оперативной памяти MS DOS © С. В. Кухта, 2010 3 1. Адресация оперативной памяти MS DOS © С. В. Кухта, 2010

4 До настоящего момента мы имели дело с переменными, которые размещаются в памяти согласно 4 До настоящего момента мы имели дело с переменными, которые размещаются в памяти согласно вполне определенным правилам. Ø Под глобальные переменные программы: ü память выделяется в процессе компиляции, ü и эти переменные существуют в течение всего времени работы программы. Ø Для локальных переменных, описанных в подпрограмме: Ø память отводится при вызове подпрограммы, Ø при выходе из нее эта память освобождается, Ø а сами переменные прекращают свое существование. Иными словами, распределение памяти во всех случаях осуществляется полностью автоматически. © С. В. Кухта, 2010

Статические данные Переменные, память под которые выделяется описанным образом, называют статическими. Под эту категорию Статические данные Переменные, память под которые выделяется описанным образом, называют статическими. Под эту категорию подпадают все переменные, объявленные в области описаний программных блоков. © С. В. Кухта, 2010 5

6 Статические данные var x, y: integer; z: float; A: array[1. . 10] of 6 Статические данные var x, y: integer; z: float; A: array[1. . 10] of float; str: string; • переменная (массив) имеет имя, по которому к ней можно обращаться • размер заранее известен (задается при написании программы) • память выделяется при объявлении • размер нельзя увеличить во время работы программы © С. В. Кухта, 2010

Динамические данные Однако Паскаль предоставляет возможность создавать новые переменные во время работы программы, сообразуясь Динамические данные Однако Паскаль предоставляет возможность создавать новые переменные во время работы программы, сообразуясь с потребностями решаемой задачи, и уничтожать их, когда надобность в них отпадает. Переменные, созданием и уничтожением которых может явно управлять программист, называют динамическими. Для более полного понимания механизма работы с динамическими переменными следует сначала разобраться в механизме адресации оперативной памяти MS DOS. © С. В. Кухта, 2010 7

8 Динамические данные • размер заранее неизвестен, определяется во время работы программы • память 8 Динамические данные • размер заранее неизвестен, определяется во время работы программы • память выделяется во время работы программы • нет имени? Проблема: как обращаться к данным, если нет имени? Решение: использовать адрес в памяти Следующая проблема: в каких переменных могут храниться адреса? как работать с адресами? © С. В. Кухта, 2010

Структура адреса в оперативной памяти Наименьшей адресуемой единицей памяти персонального компьютера, построенного на базе Структура адреса в оперативной памяти Наименьшей адресуемой единицей памяти персонального компьютера, построенного на базе микропроцессоров фирмы Intel и их аналогов, является байт. Таким образом, память представляет собой последовательность нумерованных байтов. Для обращения к конкретному байту необходимо знать его номер, который называют его физическим адресом. Память принято делить на Ø слова, Ø двойные слова Ø и параграфы. Слово имеет длину 2 байта, двойное слово – 4 байта, а параграф – 16 байт. © С. В. Кухта, 2010 9

Структура адреса в оперативной памяти При работе с памятью используется адресация по схеме «база Структура адреса в оперативной памяти При работе с памятью используется адресация по схеме «база + смещение» : При этом адрес конкретного байта М определяется как адрес некоторого заданного байта Аб (адрес базы) + расстояние до требуемого байта Асм (смещение). © С. В. Кухта, 2010 10

Структура адреса в оперативной памяти 11 В микропроцессорах фирмы Intel (начиная с i 8086) Структура адреса в оперативной памяти 11 В микропроцессорах фирмы Intel (начиная с i 8086) в качестве адреса базы используют адреса, кратные 16. Четыре последних бита (в двоичной системе счисления) такого адреса равны 0, и их не хранят, а аппаратно добавляют при вычислении физического адреса. Непрерывный участок памяти, имеющий длину не более 64 КБ и начинающийся с адреса, кратного 16 (0, 16, 32, …), называют сегментом. Адрес начала сегмента принимают за базу для всего сегмента. Адрес базы сегмента без последних четырех бит называют сегментным. © С. В. Кухта, 2010

Структура адреса в оперативной памяти Сегментный адрес и смещение имеют размер по 16 бит Структура адреса в оперативной памяти Сегментный адрес и смещение имеют размер по 16 бит (слово). Физический адрес, получаемый при их сложении с учетом отброшенных четырех бит (рис. ), имеет размер 20 бит и может адресовать память объемом 220 байт или 1 МБ. Максимальное смещение равно 216 -1, что соответствует 64 КБ памяти. Т. о. , относительно одной базы можно адресовать не более 64 КБ памяти, что и ограничивает размер сегмента. © С. В. Кухта, 2010 12

Структура адреса в оперативной памяти 13 Современные модели микропроцессоров используют адреса большей длины с Структура адреса в оперативной памяти 13 Современные модели микропроцессоров используют адреса большей длины с отличающейся схемой получения физического адреса, что учитывается версиями языка Паскаль, предназначенным для работы «под Windows» , но принцип адресации по схеме «база+смещение» используется и там. © С. В. Кухта, 2010

Виды сегментов 14 Программа и данные хранятся в памяти фрагментами, каждый из которых расположен Виды сегментов 14 Программа и данные хранятся в памяти фрагментами, каждый из которых расположен в своем сегменте. Различают три вида сегментов: Ø кодов, Ø данных Ø и стека. В сегментах кодов хранится собственно программа. В сегментах данных размещаются глобальные переменные и константы. Сегмент стека интенсивно используется в процессе выполнения программы: ü при вызове подпрограмм в стек записывается адрес возврата, ü в нем размещаются локальные переменные, ü копии параметров-значений, адреса параметров© С. В. Кухта, 2010 переменных и параметров-констант и т. п.

15 Виды сегментов В процессе работы сегментные адреса хранятся в специальных сегментных регистрах: ü 15 Виды сегментов В процессе работы сегментные адреса хранятся в специальных сегментных регистрах: ü CS – адрес базы сегмента кодов; ü DS – адрес базы сегмента данных; ü SS – адрес базы сегмента стека. Доступ к конкретным участкам сегмента осуществляется через соответствующие смещения. © С. В. Кухта, 2010

Структура записи адреса в память При записи адреса в память отдельно сохраняются сегментный адрес Структура записи адреса в память При записи адреса в память отдельно сохраняются сегментный адрес и смещение: В языке Паскаль для работы с адресами используется специальный тип данных – указатель. Данные этого типа включают два поля типа word и хранят соответственно сегментный адрес и смещение. © С. В. Кухта, 2010 16

17 2. Указатели и операции над ними © С. В. Кухта, 2010 17 2. Указатели и операции над ними © С. В. Кухта, 2010

18 Различают указатели двух типов: Ø типизированные Ø и нетипизированные. Типизированные указатели содержат адреса, 18 Различают указатели двух типов: Ø типизированные Ø и нетипизированные. Типизированные указатели содержат адреса, по которым в памяти размещаются данные определенных типов. Используя эти указатели с данными указанных типов, можно выполнять операции, предусмотренные базовым типом. Нетипизированные указатели хранят просто адреса, которые не связаны с данными конкретных типов, т. е. они могут хранить адрес переменной любого типа. © С. В. Кухта, 2010

19 Описание указателей При описании типизированного указателя необходимо сообщить компилятору, адреса переменных какого типа 19 Описание указателей При описании типизированного указателя необходимо сообщить компилятору, адреса переменных какого типа он может хранить: var <имя указателя>: ^ <тип адресуемой переменной>; Описание нетипизированного указателя: var <имя указателя>: pointer; © С. В. Кухта, 2010

20 Указатели Указатель – это переменная, в которую можно записывать адрес другой переменной (или 20 Указатели Указатель – это переменная, в которую можно записывать адрес другой переменной (или блока памяти). Объявление: указатель var p. C: ^ ^char; // адрес символа p. I: ^integer; // адрес целой переменной p. R: ^real; // адрес вещ. переменной s: ^array[1. . 10] of byte; p: pointer; // нетипизиров. указатель © С. В. Кухта, 2010

21 Указатели – единственное исключение из общего правила, согласно которому все ресурсы перед использованием 21 Указатели – единственное исключение из общего правила, согласно которому все ресурсы перед использованием должны быть описаны. Для них допускаются описания вида: Туре рр = ^реrsоn; { тип person еще не определен! } реrsоn = record { определение типа person } name: string: next: рр; end; © С. В. Кухта, 2010

22 Указатели Type { правильные объявления типов} P 1=^word; { указатель на данные типа 22 Указатели Type { правильные объявления типов} P 1=^word; { указатель на данные типа word } P 2=^char; { указатель на данные типа char } P 4=array[1. . 10] of ^real; {указатель на массив указателей, ссылающихся на данные типа real} © С. В. Кухта, 2010

23 Указатели Type { неправильные объявления типов} в таких P 5=^array[1. . 10] of 23 Указатели Type { неправильные объявления типов} в таких P 5=^array[1. . 10] of real; случаях надо прежде описать P 6=^string[25]; идентификатор типа, а P 7=^record затем использовать его Field 1 : string [15]; в других описаниях Field 2 : real; end; Type mas = array[1. . 10] of real; st = string[25]; rec = record field 1 : string [15]; field 2 : real; end; var p 5 : ^mas; p 6 © С. В. Кухта, 2010 : ^rec; : ^st; p 7

24 Указатели Для указателей, которые не хранят никаких адресов, введена константа «нулевой адрес» с 24 Указатели Для указателей, которые не хранят никаких адресов, введена константа «нулевой адрес» с именем nil. Константу nil можно присваивать указателю любого типа. © С. В. Кухта, 2010

25 Типизированные указатели Как записать адрес: var m: integer; // целая переменная p. I: 25 Типизированные указатели Как записать адрес: var m: integer; // целая переменная p. I: ^integer; // указатель A: array[1. . 2] of integer; // массив. . . p. I : = @ m; @ // адрес переменной m p. I : = @ A[1]; // адрес элемента массива A[1] p. I : = nil ; // нулевой адрес ячейки © С. В. Кухта, 2010

26 Обращение к данным через указатель program qq; var m, n: integer; p. I: 26 Обращение к данным через указатель program qq; var m, n: integer; p. I: ^integer; begin «вытащить» m : = 4; значение по адресу p. I : = @m; writeln('Адрес m = ', p. I); // вывод адреса ^ writeln('m = ', p. I^); // вывод значения n : = 4*(7 - p. I^); // n = 4*(7 - 4) = 12 p. I^ : = 4*(n - m); // m = 4*(12 – 4) = 32 end. © С. В. Кухта, 2010

27 Обращение к данным (массивы) program qq; var i: integer; A: array[1. . 4] 27 Обращение к данным (массивы) program qq; var i: integer; A: array[1. . 4] of integer; p. I: ^integer; begin for i: =1 to 4 do A[i] : = i; p. I : = @A[1]; // адрес A[1] while ( p. I^ <= 4 ) // while( A[i] <= 4 ) do begin p. I^ : = p. I^ * 2; // A[i] : = A[i]*2; p. I : = p. I + 1; // к следующему элементу end; Не работает в end. Pascal. ABC. NET! переместиться к следующему элементу = изменить адрес на sizeof(integer) © С. В. Кухта, 2010 !

28 Что надо знать об указателях Ø указатель – это переменная, в которой можно 28 Что надо знать об указателях Ø указатель – это переменная, в которой можно хранить адрес другой переменной; Ø при объявлении указателя надо указать тип переменных, на которых он будет указывать, а перед типом поставить знак ^ ; Ø знак @ перед именем переменной обозначает ее адрес; Ø запись p^ обозначает значение ячейки, на которую указывает указатель p; Ø nil – это нулевой указатель, он никуда не указывает Ø при изменении значения указателя на n он в самом деле сдвигается к n-ому следующему числу данного типа (для указателей на целые числа – на n*sizeof(integer) байт). Нельзя использовать указатель, который указывает © С. В. Кухта, 2010 неизвестно куда (будет сбой или зависание)!

29 Пример обращения к указателям Type Sym=^char; Zap=record Field 1, field 2: real; End; 29 Пример обращения к указателям Type Sym=^char; Zap=record Field 1, field 2: real; End; m=array[0. . 9] of word; Var Ch : sym; Rec : ^zap; Mas : ^m; . . . Ch^: ='*'; {обращение к динамической переменной типа char, запись в эту область символа звездочка} . . . Readln (rec^. Field 1); {обращение к полю Field 1 динамической записи, ввод в него данных с клавиатуры } . . . Writeln (Mas[5]^); {обращение к элементу Mas[5] динамич. массива, вывод на экран значения указанного элемента} . . . © С. В. Кухта, 2010

30 Следует отметить, что обращение к переменным типа Pointer (указателям, которые не указывают ни 30 Следует отметить, что обращение к переменным типа Pointer (указателям, которые не указывают ни на какой определенный тип и совместимы со всеми другими типами указателей) приводит к ошибке. Например: Var p: pointer; . . . p^: =1; {ошибка!} © С. В. Кухта, 2010

31 3. Операции над указателями © С. В. Кухта, 2010 31 3. Операции над указателями © С. В. Кухта, 2010

32 Над значениями указателей возможны следующие операции. © С. В. Кухта, 2010 32 Над значениями указателей возможны следующие операции. © С. В. Кухта, 2010

33 Присваивание При выполнении этой операции указателю присваивается значение другого указателя или nil. Допускается 33 Присваивание При выполнении этой операции указателю присваивается значение другого указателя или nil. Допускается присваивать указателю только значение того же или неопределенного типа. Например: Var р1, р2: ^integer; р3: ^real; р: pointer; {допустимые операции} p 1: =p 2; р: =р3; р1: =р; p 1: =nil; р: =nil; {недопустимые операции} р3: =р2; p 1: =p 3; © С. В. Кухта, 2010

34 Получение адреса Это унарная операция, которая строится из знака операции – символа @ 34 Получение адреса Это унарная операция, которая строится из знака операции – символа @ (коммерческое а) и одного операнда – переменной любого типа. Результат операции – указатель типа pointer, который можно присвоить любому указателю. Например: Var i: integer; pi: ^integer; . . . pi: =@i; { указатель pi будет содержать адрес переменной i } © С. В. Кухта, 2010

35 Доступ к данным по указателю Операция доступа к данным по указателю (операция разыменования) 35 Доступ к данным по указателю Операция доступа к данным по указателю (операция разыменования) необходима, чтобы получить доступ к переменной по указателю, необходимо после переменной – типизированного указателя поставить знак «^» . Полученное значение имеет тип, совпадающий с базовым типом указателя. Нетипизированные указатели разыменовывать нельзя. Например: j: =pi^; {переменной j присваивается значение целого, pi^: =pi^+2; расположенного по адресу pi } {целое значение, расположенное по адресу pi, увеличивается на 2 } © С. В. Кухта, 2010

36 В таблице показано, как выполняются операции с указателями. Фрагмент программы Const i: integer=1; 36 В таблице показано, как выполняются операции с указателями. Фрагмент программы Const i: integer=1; Var pi: ^integer; Результат операции Описание операции Создается инициализированная переменная i и указатель на целое pi pi: =@i; Указателю pi присваивается адрес переменной i © С. В. Кухта, 2010

37 Фрагмент программы pi^: =pi^+2; pi: =nil; Результат операции Описание операции Значение, адрес которого 37 Фрагмент программы pi^: =pi^+2; pi: =nil; Результат операции Описание операции Значение, адрес которого находится в pi, увеличивается на 2 Запись в pi константы «нулевой адрес» © С. В. Кухта, 2010

38 Операции отношения Из всех возможных операций отношения допускаются только операции проверки Ø равенства 38 Операции отношения Из всех возможных операций отношения допускаются только операции проверки Ø равенства (=) Ø и неравенства (< >). Эти операции проверяют соответственно равенство и неравенство адресов. Например: sign: = pl=p 2; { переменная sign логического типа получает if p 1 <> значение true или false в зависимости от значений указателей } nil then. . . { проверка адреса } © С. В. Кухта, 2010

39 Указатель на указатель Поскольку в качестве базового типа типизированного указателя может быть использован 39 Указатель на указатель Поскольку в качестве базового типа типизированного указателя может быть использован любой тип, допустимо определять «указатель на указатель» . Например, для реализации схемы, изображенной на рисунке, переменную ppi описать и инициализировать следующим образом: Const i: integer=1; Var pi: ^integer; ppi: ^pi; . . . pi: =@i; ppi: =@pi; . . . Для получения значения переменной i необходимо дважды применить операцию разыменования. В нашем случае ppi^^ имеет © С. В. Кухта, 2010 и равно 1. тип integer

40 Функции, работающие с указателями Функция ADDR(x): pointer возвращает адрес объекта х, в качестве 40 Функции, работающие с указателями Функция ADDR(x): pointer возвращает адрес объекта х, в качестве которого может быть указано имя переменной, функции, процедуры. Выполняет те же действия, что и операция «@» . © С. В. Кухта, 2010

41 Функции, работающие с указателями Функция SEG(x): word возвращает сегментный адрес указанного объекта. Функция 41 Функции, работающие с указателями Функция SEG(x): word возвращает сегментный адрес указанного объекта. Функция OFS(x): word возвращает смещение указанного объекта. © С. В. Кухта, 2010

42 Функции, работающие с указателями Функция CSEG(x): word возвращает текущее значение сегментного регистра CS 42 Функции, работающие с указателями Функция CSEG(x): word возвращает текущее значение сегментного регистра CS – сегментный адрес сегмента кодов. Функция DSEG: word возвращает текущее значение сегментного регистра DS – сегментный адрес сегмента данных. © С. В. Кухта, 2010

43 Функции, работающие с указателями Функция PTR(seg, ofs: word): pointer возвращает значение указателя по 43 Функции, работающие с указателями Функция PTR(seg, ofs: word): pointer возвращает значение указателя по заданным сегментному адресу seg и смещению ofs. © С. В. Кухта, 2010

Преобразование типов данных с использованием типизированных указателей Как отмечалось ранее, типизированный указатель связывается с Преобразование типов данных с использованием типизированных указателей Как отмечалось ранее, типизированный указатель связывается с некоторым типом данных и адресует вполне определенную область памяти, соответствующую длине внутреннего представления своего типа. Если указателям разного типа присвоить один и тот же адрес, то каждый из них будет рассматривать содержимое области в соответствии с внутренним представлением своего типа. © С. В. Кухта, 2010 44

Преобразование типов данных с использованием типизированных указателей Эта особенность указателей позволяет использовать их для Преобразование типов данных с использованием типизированных указателей Эта особенность указателей позволяет использовать их для неявного преобразования типа. Необходимо помнить, что для присвоения разнотипным указателям одного и того же адреса следует использовать нетипизированные указатели, либо задавать абсолютное значение требуемого адреса. Контроль корректности значений, полученных в результате выполненных действий, системой не осуществляется, а ложится целиком на программиста. © С. В. Кухта, 2010 45

Преобразование типов данных с использованием типизированных указателей Var L: longint; {длинное целое число} Р Преобразование типов данных с использованием типизированных указателей Var L: longint; {длинное целое число} Р 1: ^аrrаy[1. . 4] of byte; {указатель на область длиной 4 байта} k: byte; Begin L: =123456789; P 1: =@L; {возвращает нетипизированный указатель} k: =Р 1^[1]: {младший байт внутреннего представления числа L, младший потому, что числа в памяти для данного типа компьютеров хранятся с младшего байта} © С. В. Кухта, 2010 46

47 4. Управление динамической памятью © С. В. Кухта, 2010 47 4. Управление динамической памятью © С. В. Кухта, 2010

48 Куча Определяемые в примерах предыдущего параграфа указатели для наглядности содержали адреса статически размещенных 48 Куча Определяемые в примерах предыдущего параграфа указатели для наглядности содержали адреса статически размещенных переменных. Однако основное назначение указателей – адресация динамических переменных. Такие переменные располагаются в свободной области, называемой динамической памятью или «кучей» . Эта область расположена после программы, и ее объем составляет около 200. . . 300 к. Б. © С. В. Кухта, 2010

49 Куча Соответственно, чем больше объем программы, тем меньше размер свободной области памяти. Значения 49 Куча Соответственно, чем больше объем программы, тем меньше размер свободной области памяти. Значения стандартных переменных, используемых для управления динамической областью: Heap. Org – указатель на начало динамической области; Heap. End – указатель на конец динамической области; Heap. Ptr – указатель на текущее значение границы свободной динамической области. © С. В. Кухта, 2010

50 Куча Заказать и освободить память требуемого объема из динамической области можно, используя специальные 50 Куча Заказать и освободить память требуемого объема из динамической области можно, используя специальные процедуры и функции. © С. В. Кухта, 2010

Процедуры и функции, работающие с указателями Процедура New (Var <типизированный указатель>) возвращает адрес выделенного Процедуры и функции, работающие с указателями Процедура New (Var <типизированный указатель>) возвращает адрес выделенного участка памяти через параметр-переменную. Размер участка памяти определяется базовым типом указателя. Например: Var pi: ^integer; . . . New(pi); {теперь pi содержит адрес двух байт, выделенных из динамической памяти под размещение переменной целого типа} © С. В. Кухта, 2010 51

Процедуры и функции, работающие с указателями Функция New (<тип типизированного указателя>) : pointer Возвращает Процедуры и функции, работающие с указателями Функция New (<тип типизированного указателя>) : pointer Возвращает адрес выделенного участка памяти. Размер участка памяти также определяется базовым типом указателя. Например: Туре tpi: ^integer; Var pi: tpi; . . . pi: = New(tpi); {pi также содержит адрес двух байт, выделенных из динамической памяти под размещение переменной целого типа} © С. В. Кухта, 2010 52

Процедуры и функции, работающие с указателями Для размещения изученных нами типов переменных можно использовать Процедуры и функции, работающие с указателями Для размещения изученных нами типов переменных можно использовать как процедуру New, так и функцию New. Процедура New ищет в незанятой памяти подходящий по размеру кусок и, "застолбив" это место для безымянной динамической переменной, записывает в типизированный указатель адрес выделенного участка. Поэтому часто говорят, что процедура New создает динамическую переменную. © С. В. Кухта, 2010 53

Процедуры и функции, работающие с указателями Размер выделяемого Процедуры и функции, работающие с указателями Размер выделяемого "куска памяти" напрямую зависит от типа указателя. Например, если переменная p была описана Ø как указатель на integer-переменную, то процедура new(p) выделит два байта; Ø под real-переменную необходимо выделить четыре байта и т. д. © С. В. Кухта, 2010 54

Процедуры и функции, работающие с указателями Процедура Dispose (<типизированный указатель>) освобождает память по адресу, Процедуры и функции, работающие с указателями Процедура Dispose (<типизированный указатель>) освобождает память по адресу, хранящемуся в указателе. Например: Dispose(pi); При попытке применить процедуру к указателю, имеющему значение nil, или освободить уже освобожденную память фиксируется ошибка и выдается соответствующее сообщение. © С. В. Кухта, 2010 55

Процедуры и функции, работающие с указателями В результате освобождения памяти при помощи процедуры dispose Процедуры и функции, работающие с указателями В результате освобождения памяти при помощи процедуры dispose значение указателя, хранившего адрес освобожденной области, становится неопределенным. Во избежание проблем его лучше сразу же "обнулить" или присвоить другое значение: dispose(p); p: = nil; © С. В. Кухта, 2010 56

Процедуры и функции, работающие с указателями Серия последовательных обращений к New и Dispose обычно Процедуры и функции, работающие с указателями Серия последовательных обращений к New и Dispose обычно приводит к фрагментации памяти – память разбивается на небольшие фрагменты с чередованием свободных и занятых участков. В результате может возникнуть ситуация: ü свободной памяти для размещения новой переменной достаточно, ü но она не может быть размещена из-за отсутствия непрерывного участка требуемого размера. Для уменьшения явления фрагментации используют специальные процедуры. © С. В. Кухта, 2010 57

Пример 1 работы с разнотипными динамическими переменными в куче Type St 1=string[7]; St 2=string[3]; Пример 1 работы с разнотипными динамическими переменными в куче Type St 1=string[7]; St 2=string[3]; Var i, i 1, i 2, i 3, i 4: ^integer; R^: real; S 1: ^st 1; S 2: ^st 2; Begin New(i); i 1^: =1; New(i 2); i 2^: =2; New(i 3); i 3^: =3; New(i 4); i 4^: =4; (*1*) Disроsе (i 2); {освобождается второе размещение} New (i); {память нужного размера (2 байта) выделяется на 1 -м свободном месте от начала кучи, достаточном для размещения данной переменной; в этом примере - это участок, который занимала переменная i 2^, ее адрес остался в указателе i 2 } © С. В. Кухта, 2010 58

Пример 1 работы с разнотипными динамическими переменными в куче i^: =5; (*2*) Dispose(i 3); Пример 1 работы с разнотипными динамическими переменными в куче i^: =5; (*2*) Dispose(i 3); {освобождается третье размещение} New(r); {память под переменную типа real выделяется в вершине кучи, т. к. размер дырки с адресом i 3 (2 байта) мал для размещения переменной типа real, для которой необходимо 6 байт } r^: =6; (*3*) Writeln (r^); { вывод: 6. 00000 e+00} End. © С. В. Кухта, 2010 59

Пример 2 работы с разнотипными динамическими переменными в куче {В следующем примере используется массив Пример 2 работы с разнотипными динамическими переменными в куче {В следующем примере используется массив указателей } Uses crt; Var r: array [1. . 10] of ^real; i: 1. . 10; Begin Randomize; For i: =1 to 10 do begin New(r[i]); r[i]^: =random; {генерация случайных вещественных чисел в диапазоне 0 <= r[i]^ < 1} Writeln(r[i]^); {вывод случайных чисел} end; End. © С. В. Кухта, 2010 60

Процедуры и функции, работающие с указателями Процедура Mark (Var p: pointer) запоминает значение Heap. Процедуры и функции, работающие с указателями Процедура Mark (Var p: pointer) запоминает значение Heap. Ptr – указателя на текущее значение границы свободной динамической области – в указателе р, полученном в качестве параметра. © С. В. Кухта, 2010 61

Процедуры и функции, работающие с указателями Процедура Release (Var p: pointer) освобождает весь фрагмент Процедуры и функции, работающие с указателями Процедура Release (Var p: pointer) освобождает весь фрагмент памяти, начиная с адреса р, зафиксированного в указателе процедуры Mark. Например: . . . new(pl); new(p 2); mark(p); new(p 3); new(p 4); release(p); . . . Совместное использование процедур Dispose и Release недопустимо, т. к. Release разрушает список освобожденных фрагментов, создаваемый при © выполнении Dispose. С. В. Кухта, 2010 62

Процедуры и функции, работающие с указателями Динамическую память можно выделять фрагментами, указывая их размер. Процедуры и функции, работающие с указателями Динамическую память можно выделять фрагментами, указывая их размер. Процедура Get. Mem (Var p: pointer; size: word) запрашивает у системы память размера, указанного в параметре size (запрашиваемый объем не должен превышать 64 КБ), и помещает адрес выделенного системой фрагмента в переменную типа pointer с именем р (нетипизрованный указатель). Как правило, данный способ выделения памяти используется, если требуется память под размещение буферов, формат которых программисту не известен. © С. В. Кухта, 2010 63

Процедуры и функции, работающие с указателями Функция Size. Of(x): word возвращает длину указанного объекта Процедуры и функции, работающие с указателями Функция Size. Of(x): word возвращает длину указанного объекта х в байтах. © С. В. Кухта, 2010 64

Процедуры и функции, работающие с указателями Процедура Free. Mem (p: pointer; size: word) освободит Процедуры и функции, работающие с указателями Процедура Free. Mem (p: pointer; size: word) освободит в памяти столько байтов (начиная с указанного в переменной p адреса), сколько задано в переменной size, т. е. освобождает область памяти, выделенную процедурой Get. Mem. © С. В. Кухта, 2010 65

66 Обработка ошибок при выделении памяти Однако каким бы способом не запрашивалась память, может 66 Обработка ошибок при выделении памяти Однако каким бы способом не запрашивалась память, может возникнуть ситуация, когда оставшаяся свободная память меньше требуемой, и память выделить невозможно. В этом случае система по умолчанию выдает сообщение об ошибке выполнения и аварийно завершает задачу. Избежать подобной ситуации можно несколькими способами. Рассмотрим способ, который заключается в предварительной проверке наличия свободной памяти требуемого размера. © С. В. Кухта, 2010

67 Обработка ошибок при выделении памяти Рассмотрим 1 -й способ, который заключается в предварительной 67 Обработка ошибок при выделении памяти Рассмотрим 1 -й способ, который заключается в предварительной проверке наличия свободной памяти требуемого размера. Функция Maxavail: longint возвращает длину максимального непрерывного участка памяти. Функция Memavail: longint возвращает размер всей свободной памяти – сумму длин всех свободных фрагментов. © С. В. Кухта, 2010

68 Обработка ошибок при выделении памяти 2 -й способ базируется на возможности перехвата системной 68 Обработка ошибок при выделении памяти 2 -й способ базируется на возможности перехвата системной обработки ошибки выделения памяти. Для этого необходимо определить свою подпрограмму обработки ошибки, в которой вместо признака ошибки распределения динамической памяти 0, установленного по умолчанию, необходимо задать Heapfunc: =1. © С. В. Кухта, 2010

69 Обработка ошибок при выделении памяти Например: Function Heap. Func(size: word): integer; far; begin 69 Обработка ошибок при выделении памяти Например: Function Heap. Func(size: word): integer; far; begin Heap. Func: =7; end; В программе необходимо определить адрес подпрограммы обработки ошибки Неар. Еrrоr, указав собственную программу Heap. Func: Heap. Error: = @Heap. Func; . . . © С. В. Кухта, 2010

70 Обработка ошибок при выделении памяти Использование такой подпрограммы приведет к тому, что процедуры 70 Обработка ошибок при выделении памяти Использование такой подпрограммы приведет к тому, что процедуры New и Get. Mem при исчерпании памяти вернут указатели, установленные в nil, и выполнение программы не будет прервано. Действия по обработке возникшей ситуации выполняет программист, который должен проверить указатели после возврата из процедур выделения памяти. © С. В. Кухта, 2010

71 Пример. Разработать программу для определения суммы элементов двумерного массива большой размерности (n 10000, 71 Пример. Разработать программу для определения суммы элементов двумерного массива большой размерности (n 10000, m 10000, n m 50000). Для размещения массива n m вещественных чисел (real) потребуется 6 n m = 300000 байт памяти, что превышает 64 Кб, следовательно, использовать стандартный тип «массив» нельзя. Если создать массив указателей размерности n m, то потребуется 4 n m = 200000 байт памяти, что также превышает 64 Кб. © С. В. Кухта, 2010

72 Пример. Решением задачи является реализация массива в виде статически размещенного массива указателей ptrstr 72 Пример. Решением задачи является реализация массива в виде статически размещенного массива указателей ptrstr на n элементов (по числу строк), каждый указатель которого хранит адрес динамически размещенного массива – строки матрицы. © С. В. Кухта, 2010

73 Пример. Тогда одного сегмента достаточно для размещения около 64000/4 =16000 указателей на строки 73 Пример. Тогда одного сегмента достаточно для размещения около 64000/4 =16000 указателей на строки матрицы. Так как указатель может адресовать целый сегмент, то в каждой строке можно разместить 64000/6 = 10677 элементов типа real, т. е. даже больше, чем требуется по условию задачи. Однако конкретный размер массива, который можно разместить, определяется размером доступной динамической памяти (кучи). Поэтому в программе осуществляется контроль выделения памяти методом «перехвата» системной ошибки с помощью собственной функции обработки ошибок. © С. В. Кухта, 2010

74 Пример Наибольшую сложность в данной программе представляет определение адреса элемента матрицы с индексами 74 Пример Наибольшую сложность в данной программе представляет определение адреса элемента матрицы с индексами (i, j). Этот адрес складывается из сегментного адреса, хранящегося в указателе – Seg(ptrstr[i]), и смещения, состоящего из смещения начала динамического массива, которое хранится в том же указателе – Ofs(ptrstr[i]), и смещения на j– 1 элемент внутри динамически размещенного массива – (j– 1) Size. Of(real). Эти вычисления в программе целесообразно реализовать в виде подпрограммы-функции, которая возвращает результат типа real. © С. В. Кухта, 2010

75 Пример Program ex_large_mas; Const nn=16000; {максимальное количество строк} Var i, j, n, m: 75 Пример Program ex_large_mas; Const nn=16000; {максимальное количество строк} Var i, j, n, m: word; s: real; ptrstr: array[1. . nn] of pointer; {статический массив указателей на строки матрицы} Туре tpreal=^real; {функция формирования адреса элемента матрицы} Function Addr. R(I, j : word) : tpreal; begin Addr. R: =Ptr(Seg(ptrstr[i]^), Ofs(ptrstr[i]^)+(j-1)*Size. Of(real)) end; © С. В. Кухта, 2010

76 Пример {собственная функция обработки ошибок} function heapfunc(size: word): integer; far; begin heapfunc: =1; 76 Пример {собственная функция обработки ошибок} function heapfunc(size: word): integer; far; begin heapfunc: =1; end; {основная программа} begin Randomize; {подключаем собственную функцию обработки ошибок} heaperror: =@heapfunc; Write. Ln(Введите n, m'); Read. Ln(n, m); © С. В. Кухта, 2010

77 Пример for i: =1 to n do begin {запрашиваем память под строку матрицы} 77 Пример for i: =1 to n do begin {запрашиваем память под строку матрицы} Get. Mem(ptrstr[i], m*sizeof(real)); if ptrstr[i]=nil then begin {если памяти не хватает, то завершаем программу} Write. Ln('Не хватает памяти под матрицу'); for j: =1 to i-1 do {освобождаем уже выделенную память} Free. Mem(ptrstr[j], m*sizeof(real)); Halt(2); end; for j: =1 to m do Addr. R(i, j)^: =Random; {если память есть, то заполняем строку случайными числами} end; © С. В. Кухта, 2010

78 Пример s: =0; for i: =1 to n do for j: =1 to 78 Пример s: =0; for i: =1 to n do for j: =1 to m do s: =s + Addr. R(i, j)^; Write. Ln('3 нaчeнue суммы =', s: 15: 10); Write. Ln('Сpeднee значение =', s/(n*m): 15: 10); for i: =1 to n do {освобождаем использованную память} Free. Mem(ptrstr[i], m*Size. Of(real)); End. © С. В. Кухта, 2010

79 5. Динамические массивы © С. В. Кухта, 2010 79 5. Динамические массивы © С. В. Кухта, 2010

80 При работе с массивами практически всегда возникает задача настройки программы на фактическое количество 80 При работе с массивами практически всегда возникает задача настройки программы на фактическое количество элементов массива. В зависимости от применяемых средств решение этой задачи бывает различным. © С. В. Кухта, 2010

81 Первый вариант – использование констант для задания размерности массива. Program first; const n 81 Первый вариант – использование констант для задания размерности массива. Program first; const n : integer = 10; { либо n = 10; } var a : array [1. . n] of real; i : integer; begin for i: =1 to n do readln (a[i]); { и далее все циклы работы с массивом используют n} Такой способ требует перекомпиляции программы при каждом изменении числа обрабатываемых элементов. © С. В. Кухта, 2010

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

83 Program second; var a : array [1. . 25] of real; i, nf 83 Program second; var a : array [1. . 25] of real; i, nf : integer; begin writeln('введите фактич. число элементов массива <= 25 '); readln(nf); for i: =1 to nf do readln(a[i]); { и далее все циклы работы с массивом используют nf} Этот вариант более гибок и технологичен по сравнению с предыдущим, т. к. не требуется постоянная перекомпиляция программы, но очень нерационально расходуется память, ведь ее объем для массива всегда выделяется по указанному максимуму. Используется же © С. В. Кухта, 2010 только часть ее.

84 Вариант третий – в нужный момент времени надо выделить динамическую память в требуемом 84 Вариант третий – в нужный момент времени надо выделить динамическую память в требуемом объеме, а после того, как она станет не нужна, освободить ее. © С. В. Кухта, 2010

85 Program dynam_memory; type mas = array[1. . 2] of <требуемый_тип_элемента>; ms = ^mas; 85 Program dynam_memory; type mas = array[1. . 2] of <требуемый_тип_элемента>; ms = ^mas; var a : ms; i, nf : integer; begin writeln('введите фактич. число элементов массива'); readln(nf); getmem (a, sizeof(<требуемый_тип_элемента>) *nf); for i: =1 to nf do readln(a^[i]); { и далее все циклы работы с массивом используют nf}. . . freemem(a, sizeof(<требуемый_тип_элемента>) *nf); end. © С. В. Кухта, 2010

86 Где нужны динамические массивы? Задача. Ввести размер массива, затем – элементы массива. Отсортировать 86 Где нужны динамические массивы? Задача. Ввести размер массива, затем – элементы массива. Отсортировать массив и вывести на экран. Проблема: размер массива заранее неизвестен. Пути решения: 1) выделить память «с запасом» ; 2) выделять память тогда, когда размер стал известен. Алгоритм: 1) ввести размер массива; 2) выделить память ; выделить память 3) ввести элементы массива; 4) отсортировать и вывести на экран; 5) удалить массив © С. В. Кухта, 2010

87 Использование указателей какой-то массив целых чисел program qq; type int. Array = array[1. 87 Использование указателей какой-то массив целых чисел program qq; type int. Array = array[1. . 1] of integer; var A: ^int. Array; i, N: integer; begin writeln('Размер массива>'); выделить память readln(N); Get. Mem(pointer(A), N*sizeof(integer)); for i : = 1 to N do readln(A[i]); работаем так же, . . . { сортировка } как с обычным массивом! for i : = 1 to N do writeln(A[i]); Free. Mem(pointer(A)); освободить память end. © С. В. Кухта, 2010

88 Использование указателей • для выделения памяти используют процедуру Get. Mem( указатель, размер в 88 Использование указателей • для выделения памяти используют процедуру Get. Mem( указатель, размер в байтах ); • указатель должен быть приведен к типу pointer – указатель без типа, просто адрес какого-то байта в памяти; • с динамическим массивом можно работать так же, как и с обычным (статическим); • для освобождения блока памяти нужно применить процедуру Free. Mem: Free. Mem ( указатель ); © С. В. Кухта, 2010

89 Пример 1 Рассмотрим пример использования динамического одномерного массива, который используется как двумерный массив. 89 Пример 1 Рассмотрим пример использования динамического одномерного массива, который используется как двумерный массив. После ввода реальной размерности массива и выделения памяти для обращения к элементу двумерного массива адрес его рассчитывается, исходя из фактической длины строки и положения элемента в строке (при заполнении матрицы по строкам). Требуется найти максимальный элемент в матрице и его координаты. © С. В. Кухта, 2010

90 Пример 1 Uses crt; type t 1=array[1. . 1] of integer; var a: 90 Пример 1 Uses crt; type t 1=array[1. . 1] of integer; var a: ^t 1; n, m, i, j, k, p: integer; max: integer; begin write('n='); readln (n); write('m='); readln (m); getmem (a, sizeof(integer)*n*m); for i: =1 to n*m do read(a^[i]); max: =a^[1]; k: =1; p: =1; for i: =1 to n do for j: =1 to m do if a^[(i-1)*m+j] > max then begin max: =a^[(i-1)*m+j]; k: =i; p: =j end; write('строка=', k: 2, ' столбец=', p: 2); freemem(a, 2*n*m); © С. В. Кухта, 2010 end.

91 Пример 2 В следующем примере для хранения двумерного массива используется одномерный массив указателей 91 Пример 2 В следующем примере для хранения двумерного массива используется одномерный массив указателей на столбцы. В задаче требуется найти столбцы матрицы, в которых находятся минимальный и максимальный элементы матрицы и если это разные столбцы, то поменять их местами. © С. В. Кухта, 2010

92 Пример 2 Uses crt; Type Vk=^t 1; T 1=array[1. . 1] of integer; 92 Пример 2 Uses crt; Type Vk=^t 1; T 1=array[1. . 1] of integer; Mt=^t 2; T 2=array[1. . 1] of vk; Var a: mt; m, n, i, j, k, l: integer; max, min: integer; r: pointer; Begin Readln (n, m); {выделение памяти под указатели столбцов матрицы} Getmem(a, sizeof(pointer)*m); {выделение памяти под элементы столбцов} for j: =1 to m do Getmem (a^[j], sizeof(integer)*n); © С. В. Кухта, 2010

93 Пример 2 For i: =1 to n do For j: =1 to m 93 Пример 2 For i: =1 to n do For j: =1 to m do Read(a^[j]^[i]); For i: =1 to n do begin For j: =1 to m do Write (a^[j]^[i]: 4); Writeln End; max: =a^[1]; k: =1; min: =max; l: =1; For j: =1 to m do For i: =1 to n do if a^[j]^[i]max then begin max: =a^[j]^[i]; k: =j; end; © С. В. Кухта, 2010

94 Пример 2 {для обмена столбцов достаточно поменять указатели на столбцы} if k<>l then 94 Пример 2 {для обмена столбцов достаточно поменять указатели на столбцы} if k<>l then begin r: =a^[k]; a^[k]: =a^[l]; a^[l]: =r end; For i: =1 to n do begin For j: =1 to m do Write(a^[j]^[i]: 3, ' '); Writeln end; For i: =1 to m do Freemem (a^[i], n*sizeof(integer)); Freemem (a, m*sizeof(pointer)) End. © С. В. Кухта, 2010

95 Ошибки при работе с памятью Запись в «чужую» область памяти: память не была 95 Ошибки при работе с памятью Запись в «чужую» область памяти: память не была выделена, а массив используется. Что делать: так не делать. Выход за границы массива: обращение к элементу массива с неправильным номером, при записи портятся данные в «чужой» памяти. Что делать: если позволяет транслятор, включать проверку выхода за границы массива. Указатель удаляется второй раз: структура памяти нарушена, может быть все, что угодно. Что делать : в удаленный указатель лучше записывать nil, ошибка выявится быстрее. Утечка памяти: ненужная память не освобождается. Что делать : убирайте «мусор» © С. В. Кухта, 2010 (в среде. NET есть сборщик мусора!)

96 6. Динамические записи © С. В. Кухта, 2010 96 6. Динамические записи © С. В. Кухта, 2010

97 Структуры (в Паскале – записи) Свойства: Задача: объединить эти данные в единое • 97 Структуры (в Паскале – записи) Свойства: Задача: объединить эти данные в единое • автор (строка) целое • название (строка) • год издания (целое число) • количество страниц (целое число) Структура (запись) – это тип данных, который может включать в себя несколько полей – элементов разных типов (в том числе и другие структуры). Размещение в памяти автор название год издания количество страниц 40 символов 80 символов целое © С. В. Кухта, 2010

98 Одна запись Объявление (выделение памяти): название запись поля var Book: record author: string[40]; 98 Одна запись Объявление (выделение памяти): название запись поля var Book: record author: string[40]; title: string[80]; year: integer; pages: integer; end; // // автор, строка название, строка год издания, целое кол-во страниц, целое ! Обращение к полям: Для обращения к полю записи используется точка! readln(Book. author); // ввод readln(Book. title); Book. year : = 1998; // присваивание if Book. pages > 200 then // сравнение writeln(Book. author, '. ', Book. title); // вывод © С. В. Кухта, 2010

99 Массив записей Books[1] . . . author title Books[10] year Объявление (выделение памяти): 99 Массив записей Books[1] . . . author title Books[10] year Объявление (выделение памяти): const N = 10; var a. Books: array[1. . N] of record author: string[40]; title: string[80]; year: integer; pages: integer; end; © С. В. Кухта, 2010 pages

100 Массив записей Обращение к полям: for i: =1 to N do begin readln(a. 100 Массив записей Обращение к полям: for i: =1 to N do begin readln(a. Books[i]. author); readln(a. Books[i]. title); . . . end; for i: =1 to N do if a. Books[i]. pages > 200 then writeln(a. Books[i]. author, '. ', a. Books[i]. title); ! a. Books[i]. author – обращение к полю author записи a. Books[i] © С. В. Кухта, 2010

101 Новый тип данных – запись Объявление типа: type TBook = record author: string[40]; 101 Новый тип данных – запись Объявление типа: type TBook = record author: string[40]; title: string[80]; year: integer; pages : integer; end; ! // // Память не выделяется! автор, строка название, строка год издания, целое кол-во страниц, целое TBook – Type Book ( «тип книга» ) – удобно! Объявление переменных и массивов: const N = 10; var Book: TBook; // одна запись a. Books: array[1. . N] of TBook; © С. В. Кухта, 2010 // массив

102 Записи в процедурах и функциях Процедура: procedure Show. Author ( b: TBook ); 102 Записи в процедурах и функциях Процедура: procedure Show. Author ( b: TBook ); begin writeln ( b. author ); end; Функция: function Is. Old( b: TBook ): boolean; begin Is. Old : = b. year < 1900; Память не end; ! Основная программа: Book. author : = 'А. С. Пушкин'; Show. Author ( Book ); Book. year : = 1800; writeln( Is. Old(Book) ); © С. В. Кухта, 2010 выделяется!

103 Файлы записей Объявление указателя на файл: var F: file of TBook; Запись в 103 Файлы записей Объявление указателя на файл: var F: file of TBook; Запись в файл: Assign(F, 'books. dat'); { связать с указателем } Rewrite(F); { открыть файл для запись } writeln(F, Book); { запись } for i: =1 to 5 do writeln(a. Book[i]); { запись } Close(F); { закрыть файл } © С. В. Кухта, 2010

104 Чтение из файла Известное число записей: Assign(F, 'books. dat'); { связать с указателем 104 Чтение из файла Известное число записей: Assign(F, 'books. dat'); { связать с указателем } Reset(F); { открыть для чтения } Read(F, Book); { чтение } for i: =1 to 5 do Read(F, a. Book[i]); { чтение } Close(F); { закрыть файл } «Пока не кончатся» : пока не дошли до конца файла F EOF = end of file count : = 0; while not eof(F) do begin count : = count + 1; { счетчик } Read(F, a. Book[count]); { чтение } end; ? В чем может быть проблема! © С. В. Кухта, 2010

105 Пример программы Задача: в файле books. dat записаны данные о книгах в виде 105 Пример программы Задача: в файле books. dat записаны данные о книгах в виде массива структур типа TBook (не более 100). Установить для всех 2008 год издания и записать обратно в тот же файл. полное описание type TBook … ; Tbook структуры const MAX = 100; var a. Books: array[1. . MAX] of TBook; i, N: integer; F: file of TBook; begin { прочитать записи из файла, N - количество } for i: =1 to N do a. Books[i]. year : = 2008; { сохранить в файле } end. © С. В. Кухта, 2010

106 Пример программы Чтение «пока не кончатся» : чтобы не выйти за пределы массива 106 Пример программы Чтение «пока не кончатся» : чтобы не выйти за пределы массива Assign(f, 'books. dat'); Reset(f); N : = 0; while not eof(F) and (N < MAX) do begin N : = N + 1; read(F, a. Books[N]); end; Сlose(f); Сохранение: Assign(f, 'books. dat'); { можно без этого } Rewrite(f); for i: =1 to N do write(F, a. Books[i]); Close(f); © С. В. Кухта, 2010

107 Выделение памяти под запись переменнаяуказатель на TBook var p. B: ^TBook; begin выделить 107 Выделение памяти под запись переменнаяуказатель на TBook var p. B: ^TBook; begin выделить память под запись, записать адрес в p. B New(p. B); p. B^. author : = 'А. С. Пушкин'; p. B^. title : = 'Полтава'; p. B^. year : = 1990; ! p. B^. pages : = 129; Dispose(p. B); end. освободить память © С. В. Кухта, 2010 Для обращения к полю записи по адресу используется знак ^

108 Сортировка массива записей Ключ (ключевое поле) – это поле записи (или комбинация полей), 108 Сортировка массива записей Ключ (ключевое поле) – это поле записи (или комбинация полей), по которому выполняется сортировка. const N = 100; var a. Books: array[1. . N] of TBook; i, j, N: integer; temp: TBook; { для обмена } begin { заполнить массив a. Books } { отсортировать = переставить } for i: =1 to N do writeln(a. Books[i]. title, a. Books[i]. year: 5); end. © С. В. Кухта, 2010

109 Сортировка массива записей for i: =1 to N-1 do for j: =N-1 downto 109 Сортировка массива записей for i: =1 to N-1 do for j: =N-1 downto i do if a. Books[j]. year > a. Books[j+1]. year then begin temp : = a. Books[j]; a. Books[j] : = a. Books[j+1]; a. Books[j+1] : = temp; end; ? Какой ключ сортировки? ? ? Какой метод сортировки? © С. В. Кухта, 2010 Что плохо?

110 Сортировка массива записей Проблема: как избежать копирования записи при сортировке? Решение: использовать вспомогательный 110 Сортировка массива записей Проблема: как избежать копирования записи при сортировке? Решение: использовать вспомогательный массив указателей, при сортировке переставлять указатели. До сортировки: После сортировки: p[1] 5 p[5] 5 p[2] 1 p[1] 1 p[3] 3 p[4] 2 p[2] 2 Вывод результата: for i: =1 to N do p[i]^ writeln(p[i]^. title, p[i]^. year: 5); © С. В. Кухта, 2010 p[5] 4 p[4] 4

111 Реализация в программе type PBook = ^TBook; { новый тип данных } var 111 Реализация в программе type PBook = ^TBook; { новый тип данных } var p: array[1. . N] of PBook; вспомогательные begin указатели { заполнение массива записей} начальная for i: =1 to N do расстановка p[i] : = @a. Books[i]; for i: =1 to N-1 do for j: =N-1 downto i do if p[j]^. year > p[j+1]^. year then begin temp : = p[j]; меняем только p[j] : = p[j+1]; указатели, записи p[j+1] : = temp; остаются на местах end; for i: =1 to N do writeln(p[i]^. title, p[i]^. year: 5); © С. В. Кухта, 2010 end.