русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

Нетипизированные указатели

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

VAR p1:^BYTE;
p2: ^WORD;
pp:POINTER;

pp:=p1; pp:=p2

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

GetMem(p,size) – захватить size байт и записать адрес первого из них в указатель p;

FreeMem(p,size) – освободить size байт и записать в р значение NIL.

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

Яркий пример использования нетипизированных указателей – создание анимации. Для организации движения участка картинки этот участок надо где-то запомнить. Поскольку заранее требуемый размер памяти для хранения картинки неизвестен (а он зависит от размеров в картинки и числа цветов на экране), будем сохранять информацию в динамической памяти. Тип данных в данном случае не важен – программа просто скопирует заданное количество байт из видеопамяти в динамическую и обратно.

s:=ImageSize(10,10,50,50);
GetMem(p,s);
GetImage(10,10,50,50,p^);
FOR i:=0 TO 600 DO
BEGIN
PutImage(i,240,p^,XORPut);
DELAY(100);
PutImage(i,240,p^,XORPut)
END;
CloseGraph;
FreeMem(p,s)
END.

А что, если в программе нужно завести очень большой массив? Динамической памяти у нас много. Давайте заведем массив на 1000000 вещественных чисел. Оценим размер такого массива. Одна переменная типа REAL занимает 4 байта. Тогда весь массив займет 1000000 x 4 / 1024 = 3906Кб = 3,8 Мб. Казалось бы, не так много. Попробуем описать такой массив в программе:

TYPE TA=ARRAY[1..1000000] OF REAL;
VAR a:^TA;

Увы, ничего не выйдет. Паскаль выдаст сообщение об ошибке "Structure too large" – слишком большая структура данных. Ведь старого ограничения на размер массива в 64Кб никто не отменял! Как же быть? Какой смысл в сотнях мегабайт оперативной памяти, если в ней можно разместить только жалкие 64Кб? В следующем разделе мы узнаем ответ на этот вопрос.

14. Линейные списки: основные виды и способы реализации

Итак, как мы узнали на предыдущей лекции, создать большой (больше, чем 64Кб) массив в динамической памяти все равно не удается. Как же тогда работают программы, обрабатывающие десятки и сотни мегабайт данных? Оказывается, есть оригинальный способ создания больших массивов, состоящий в размещении в памяти "гибкого" массива с произвольным числом элементов (Рис. 10.1).

Рис. 14.1. Большой массив в динамической памяти.

Каждый элемент такого массива состоит из двух частей: в одной записаны хранимые в этом элементе данные и некий идентификатор элемента (скажем, порядковый номер), а в другой – адрес следующего элемента массива. Получается своеобразная цепочка. При этом для работы с таким массивов достаточно иметь лишь одну статическую переменную, в которой будет храниться адрес первого элемента массива ("головы"). У последнего же элемента массива ("хвоста") адрес следующего элемента будет равен пустому значению – NIL.

Очевидно, каждый элемент такого массива надо представить в виде записи, поскольку он должен объединять данные разного типа. Попробуем это сделать, создав массив для хранения текстовых строк (типа STRING):

TYPE Tnode=RECORD
number:WORD;
data: STRING;
next: ^???
END;

Поле number будет содержать порядковый номер элемента массива, поле data – собственно текстовую строку, а вот поле next должно быть указателем. С указателем связан конкретный тип данных. В рассматриваемом случае указатель должен указывать на следующий элемент массива, который тоже будет типа Tnode. Казалось бы, надо написать next:^Tnode. Но Паскаль этого не позволяет! Описание типа Tnode еще не окончено, а мы внутри этого описания ссылаемся на еще неописанный тип. Это явное нарушение краеугольного принципа Паскаля, гласящего: «Все упоминаемые в программе объекты (переменные, типы, константы, процедуры, функции) должны быть предварительно описаны».

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

TYPE Ptr = ^Tnode; { указатель на еще не описанный тип }
TYPE Tnode=RECORD
number: WORD;
data: STRING;
next: Ptr
END;

За счет некоторого отступления от жестких правил Паскаля удалось создать необходимый тип данных. Что дальше? Прежде всего, нужна статическая переменная, через которую из программы можно будет работать с массивом. Объявим ее:

VAR p:Ptr;

Теперь все готово для добавления в массив новых элементов. В начальный момент массив пуст, p=NIL. Для добавления элемента в "голову" массива нужно выполнить следующие действия:

1. Выделить память под новый элемент массива и запомнить указатель на него..
2. Присвоить полю next нового элемента значение p.
3. Присвоить p значение указателя на новый элемент.

Все происходящее показано на Рис. 10.2 (Адрес1 – адрес первого элемента массива).

Рис. 14.2. Добавление элемента в "голову" массива.

Программируется это так:

VAR q:Ptr;
BEGIN

{ выделили память и запомнили указатель на нее в вспомогательной переменной q}

New(q);

{ старая ссылка на голову (р) идет в хвост }
q^.Ptr:=p;

{ теперь голова указывает на новый первый элемент }
p:=q

Важен порядок выполнения этих операторов – если их поменять местами, ничего не получится.

Просмотров: 588


Вернуться в оглавление



Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.