Особенности работы с указателями и адресами
Исследование особенностей использования динамической памяти для размещения крупных массивов данных. Описания операции получения значения переменной по ее адресу. Адресация динамических переменных через указатели. Объявление указателей в Турбо Паскале.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | реферат |
Язык | русский |
Дата добавления | 03.06.2014 |
Размер файла | 27,1 K |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Размещено на http://www.allbest.ru/
Введение
Адресация динамических переменных происходит через указатели. В Паскале можно определить переменные, которые имеют тип указатель, их значения определяют адрес объекта. Для работы с динамическими переменными в программе должны быть предусмотрены:
выделение памяти под динамическую переменную;
присвоение указателю на динамическую переменную адреса выделенной памяти (инициализация указателя);
освобождение памяти после использования динамической переменной.
Указатели. Описание указателей
Указатели - это особый тип данных. В переменных этого типа хранятся адреса других переменных содержащих полезную для программы информацию. На первый взгляд может показаться, что использование указателей приводит к лишним затратам памяти и к усложнению программы, а также существе усложняет и сам процесс программирования. В данной главе мы, приведем такие примеры использования указателей, из которых станет ясно, что все дополнительные затраты на их хранение и обработку окупаются в полной мере. Работа с указателями предусмотрена не только в Pascal, но и в некоторых других языках программирования. Например, в языке С указатели используются практически в любой программе.
В Pascal роль указателей несколько скромнее, и, тем не менее, начинающим программистам следует усвоить базовые принципы работы с указателями, чтобы глубже понять внутренний механизм обработки и выполнения любой компьютерной программы.
Указатели и адреса
Известно, что адресом переменной является адрес первого байта ячейки памяти, которая под нее отводится. Для данных структурных типов (массивов и записей) их адресом считается адрес первого байта первого элемента.
В Turbo Pascal существует возможность прямого доступа к любому байту оперативной памяти по его адресу при помощи определенных в модуле system массивов Mem, MemW и MemL, которые позволяют записать информацию или прочитать ее непосредственно из ячеек памяти (один, два или четыре байта). Это очень опасные действия, поэтому они исключены в 32- разрядных системах программирования. Все же дадим краткие пояснения для тех, кто работает в среде Borland (Turbo) Pascal.
В качестве индекса в этих массивах используется адрес, записанный в виде, принятом в DOS: сегмент: Смещение относительно начала сегмента. Такой странный способ записи адреса связан с тем, что в операционной системе DOS вся память разбита на сегменты, размеры которых не превышают 64 Кбайт. Для получения абсолютного адреса из пары сегмент Смещение система прибавляет к сегменту справа шестнадцатеричный ноль (это четыре нуля в двоичной системе), а затем складывает его со смещением. Таким способом можно адресовать 1 Мбайт памяти.
Например, начальный адрес видеобуфера запишется в виде $B800:$000, а обратиться к самому первому его байту можно так: [$В800:$0000],
к первым двум байтам - MemW[$B800:$0000],
к первым четырем байтам - MemL [$B800:$0000]
Абсолютный адрес, соответствующий данной паре, - $B8000.
Еще один пример для любознательных - оператор mem[0:$41C]:=mem[0:$41А]; можно применить для принудительной очистки буфера клавиатуры. Здесь адрес маркера конца буфера клавиатуры приравнивается к адресу его начала. Конечно, в данном случае лучше воспользоваться средствами модуля crt.
Имеется еще один способ обращения к оперативной памяти - использование служебного слова absolute при описании переменной. В этом случае переменная будет располагаться именно по тому адресу в оперативной памяти, который указан после absolute. Разумеется, использование служебного слова absolute - столь же опасный способ, как и обращение к памяти через предопределенные массивы.
Однако absolute может использоваться и более безопасным способом, позволяя совмещать в памяти две переменные с разными именами. В языке Pascal есть специальная операция получения указателя на переменную (или процедуру) - она обозначается как @. Имеется также эквивалентная ей функция addr.
Например, @x или addr(х) - адрес переменной х.
Имеется и обратная операция получения значения переменной по ее адресу, которая обозначается знаком ^. Например, р^ переменная с адресом р.
В повседневной практике средства работы с адресами используются довольно редко. Основное назначение указателей состоит в том, чтобы обеспечить механизм использования в программе динамических переменных. Этот механизм мы и будем обсуждать подробно в следующих разделах.
В Pascal имеются два различных вида указателей: типизированные и нетипизированные. Типизированный указатель - это указатель на переменную определенного типа, например, целого, строкового или типа массива Нетипизарованный указатель - это адрес первого байта области памяти, в которой может размещаться любая информация вне зависимости от ее типа.
Описание двух видов указателей выполняется по-разному:
p1: ^integer; {указатель на переменную целого типа}: ^string; {указатель на стоку}pointer; {нетипизированный указатель}
Заметим что тип pointer совместим со всеми типами указателей. В дальнейшем изложении для удобства имена всех указателей будем начинать с буквы p (pointer).
Каждый указатель размещается в сегменте данных или в стеке (если он объявлен в подпрограмме) и занимает там 4 байта. Это дополнительные “накладные расходы' памяти. Поэтому обычные переменные очень редко создают и уничтожают динамически, оставляя эту возможность для больших совокупностей данных.
Чем больше размер динамической переменной, тем меньше доля накладных расходов. Например, при хранении в динамической памяти массивов больших размеров лишние 4 байта, затраченные на указатель, несущественны.
Объявление указателей
Как правило, в Турбо Паскале указатель связывается с некоторым типом данных. Такие указатели будем называть типизированными. Для объявления типизированного указателя используется значок А, который помещается перед соответствующим типом, например:
var: ^integer;
р2: ^real;= ^PerconRecord;= record: string;: string;: PerconPointer;
Обратите внимание: при объявлении типа PerconPointer мы сослались на PerconRecord, который предварительно в программе объявлен не был. Как уже отмечалось, в Турбо Паскале последовательно проводится в жизнь принцип, в соответствии с которым перед использованием какого-либо идентификатора он должен быть описан. Исключение сделано только для указателей, которые могут ссылаться на еще не объявленный тип данных. Это исключение сделано не случайно. Динамическая память дает возможность реализовать широко используемую в некоторых программах организацию данных в виде списков. Каждый элемент списка имеет в своем составе указатель на соседний элемент, что обеспечивает возможность просмотра и коррекции списка. Если бы в Турбо Паскале не было этого исключения, реализация списков была бы значительно затруднена.
В Турбо Паскале можно объявлять указатель и не связывать его при этом с каким-либо конкретным типом данных. Для этого служит стандартный тип POINTER, например: р: pointer;
Указатели такого рода будем называть нетипизированными. Поскольку нетипизированные указатели не связаны с конкретным типом, с их помощью удобно динамически размещать данные, структура и тип которых меняются в ходе работы программы.
Как уже говорилось, значениями указателей являются адреса переменных в памяти, поэтому следовало бы ожидать, что значение одного указателя можно передавать другому. На самом деле это не совсем так. В Турбо Паскале можно передавать значения только между указателями, связанными с одним и тем же типом данных. Если, например,
var
p1, p2: ^integer;
р3: ^real;
рр: pointer;
то присваивание
р1:= р2;
вполне допустимо, в то время как
р1:= р3;
запрещено, поскольку Р1 и Р3 указывают на разные типы данных. Это ограничение, однако, не распространяется на нетипизированные указатели, поэтому мы могли бы записать
:= р3;
р1:= рр;
и тем самым достичь нужного результата.
Читатель вправе задать вопрос, стоило ли вводить ограничения и тут же давать средства для их обхода. Все дело в том, что любое ограничение, с одной стороны, вводится для повышения надежности программ, а с другой - уменьшает мощность языка, делает его менее пригодным для каких-то применений. В Турбо Паскале немногочисленные исключения в отношении типов данных придают языку необходимую гибкость, но их использование требует от программиста дополнительных усилий и таким образом свидетельствует о вполне осознанном действии.
Использование указателей
динамический память указатель адресация
Подведем некоторые итоги. Итак, динамическая память составляет 200...300 Кбайт или больше, ее начало хранится в переменной HEAPORG, a конец соответствует адресу переменной HEAPEND. Текущий адрес свободного участка динамической памяти хранится в указателе HEAPPTR.
Посмотрим, как можно использовать динамическую память для размещения крупных массивов данных. Пусть, например, требуется обеспечить доступ к элементам прямоугольной матрицы 100х200 типа EXTENDED. Для размщеения такого массива требуется память 200000 байт (100*200*10). Казалось бы, эту проблему можно решить следующим образом:
var,j: integer;: array [1..100, 1..200] of ^real;i := 1 to 100 doj := 1 to 200 do(PtrArr[i,j]);.
Теперь к любому элементу вновь созданного динамического массива можно обратиться по адресу, например:
PtrArr[1,1]^ := 0;PtrArr[i,j*2]^ > 1 then
Вспомним, однако, что длина внутреннего представления указателя составляет 4 байта, поэтому для размещения массива PTRARR потребуется 100*200*4 = 80000 байт, что превышает размер сегмента данных (65536 байт), доступный, как уже отмечалось, программе для статического размещения данных. Выходом из положения могла бы послужить адресная арифметика, т.е. арифметика над указателями, потому что в этом случае можно было бы отказаться от создания массива указателей PTRARR и вычислять адрес любого элемента прямоугольной матрицы непосредственно перед обращением к нему. Однако в Турбо Паскале над указателями не определены никакие операции, кроме операций присвоения и отношения.
Тем не менее, решить указанную задачу все-таки можно. Как мы уже знаем, любой указатель состоит из двух слов типа WORD, в которых хранятся сегмент и смещение. В Турбо Паскале определены две встроенные функции типа WORD, позволяющие получить содержимое этих слов:(X) - возвращает сегментную часть адреса;(X) - возвращает смещение.
Аргументом Х при обращении к этим функциям может служить любая переменная, в том числе и та, на которую указывает указатель. Например, если имеем
var
р: ^real;(p);^ := 3.14;
то функция SEG(P) вернет сегментную часть адреса, по которому располагается 4-байтный указатель Р, в то время как SEG(P^) - сегмент 6-байтного участка кучи, в котором хранится число 3.14.
С другой стороны, с помощью встроенной функции PTR(SEG,OFS: WORD): POINTER можно создать значение указателя, совместимое с указателями любого типа. Таким образом, возможна такая последовательность действий. Вначале процедурой GETMEM из кучи забираются несколько фрагментов подходящей длины (напомню, что за одно обращение к процедуре можно зарезервировать не более 65521 байт динамической памяти). Для рассматриваемого примера удобно резервировать фрагменты такой длины чтобы в них могли, например, разместиться строки прямоугольной матрицы, т.е. * 10 = 2000 байт.
Начало каждого фрагмента, т.е. фактически начало размещения в памяти каждой строки, запоминается в массиве PTRSTR, состоящем из 100 указателей. Теперь для доступа к любому элементу строки нужно вычислить смещение этого элемента от начала строки и сформировать соответствующий указатель:
var, j: integer;: array [1..100] of pointer;: ^real;= 6;i := 1 to 100 do(PtrStr[i],SizeOfReal*200);
{Обращение к элементу матрицы [i,j]}
pr := ptr(seg(PtrStr[i]^), ofs(PtrStr[i]^)+(j-1)*SizeOfReal);
if pr^ > 1 then
Поскольку оператор вычисления адреса PR := PTR... будет, судя по всему, использоваться в программе неоднократно, полезно ввести вспомогательную функцию GETR, возвращающую значение элемента матрицы, и процедуру PUTR, устанавливающую новое значение элемента. Каждая из них, в свою очередь, обращается к функции ADDRR для вычисления адреса. Ниже приводится программа, создающая в памяти матрицу из NxM случайных чисел и вычисляющая их среднее значение.
program Primer1; = 6; {Длина переменной типа REAL}= 100; {Количество столбцов}
М = 200; {Количество строк}
var, j: integer;: array [1..N] of pointer;: real;= ^real;
{}AddrR(i,j: word): RealPoint;
{По сегменту i и смещению j выдает адрес вещественной переменной}
begin:= ptr(seg(PtrStr[i]^), ofs(PtrStr[i]^)+(j-1)*SizeOfReal); {AddrR}
{}GetR(i,j: integer): real;
{Выдает значение вещественной переменной по сегменту i и смещению j ее адреса}
begin:= AddrR(i,j)^; {GetR}
{}PutR(i,j: integer; x: real);
{Помещает в переменную, адрес которой имеет сегмент i смещение j, вещественное значение x}
begin(i,j)^ := x; {PutR}
{}{Main}i := 1 to N do(PtrStr[i], M*SizeOfReal);j := 1 to M do PutR(i, j, Random);:= 0;i := 1 to N doj := 1 to M do:= s + GetR(i,j);(s / (N * M): 12:10). {Main}
В рассмотренном примере предполагается, что каждая строка размещается в куче, начиная с границы параграфа, и смещение для каждого указателя PTRSTR равно нулю. В действительности при последовательных обращениях к процедуре GETMEM начало очередного фрагмента следует сразу за концом предыдущего и может не попасть на границу сегмента. В результате, при размещении фрагментов максимальной длины (65521 байт) может возникнуть переполнение при вычислении смещения последнего байта.
Размещено на Allbest.ru
Подобные документы
Структура программы в Турбо Паскале и определение переменной в ней. Понятие идентификатора и его основные ограничения. Операторы присваивания в языке программирования. Процедура ввода-вывода информации. Способы описания массива, обработка его элементов.
контрольная работа [134,5 K], добавлен 28.09.2012Правила описания множественных типов данных, приемов использования множеств и операций над множествами в Паскаль-программах. Разработка в Турбо Паскале программы вывода всех согласных букв, которые входят хотя бы в одно слово заданного предложения.
контрольная работа [30,8 K], добавлен 25.12.2010Структура – это объединение одного либо более объектов (переменных, массивов, указателей, других структур). Понятие структурной переменной. Создание массивов структур. Использование вложенных структур в виде элементов массивов person, date, pibm.
лабораторная работа [17,6 K], добавлен 15.07.2010Ознакомление с понятием, особенностями объявления, инициализацией и принципами работы с одномерными и двумерными массивами. Изучение смысла тернарной операции вывода элементов матрицы. Рассмотрение сущности и способов использования указателей переменных.
лабораторная работа [22,1 K], добавлен 15.07.2010Разработка программ на языке Turbo Pascal на основе использования массивов данных. Особенности хранения данных, способы объявления переменных, действия над элементами массивов, их ввод и вывод. Практическое применение одномерных и многомерных массивов.
методичка [17,8 K], добавлен 25.11.2010Система программирования Турбо Паскаль. Главные особенности языка С++. Составной и условный оператор в Паскале, алгоритм работы. Метка в Турбо Паскале. Счетный оператор цикла FOR. Описание логической структуры. Свойства функции PieSlice и initgraph.
курсовая работа [20,8 K], добавлен 23.12.2010Средства создания динамических структур данных. Формат описания ссылочного типа. Структура памяти во время выполнения программы. Линейные списки, стек, очередь. Организация списков в динамической памяти. Пример создания списка в обратном порядке.
лабораторная работа [788,2 K], добавлен 14.06.2009Инструкции выбора if, switch, цикла for, while, do-while, перехода break, continue, goto и возврата return. Понятие и функциональные особенности указателя, операции над ними, передача параметров функции. Связь массивов и указателей – генерация указателя.
презентация [94,8 K], добавлен 19.10.2014Указатель — переменная, диапазон значений которой состоит из адресов ячеек памяти специального значения - нулевого адреса; применение указателя для доступа к области с динамическим размещением памяти (кучи); выгоды косвенной инициализации и адресации.
реферат [27,3 K], добавлен 06.06.2011Способы ограждения пользователей от деталей фактического устройства данных. Список описателей переменных, указателей или массивов. Статические или динамические структуры данных. Доступ к различным элементам данных. Добавление и удаление элементов.
презентация [57,8 K], добавлен 14.10.2013