Поможем написать учебную работу
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.

Предоплата всего

Подписываем
Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.
Предоплата всего
Подписываем
Указатели
Изложим материал как вам давали на занятиях
Работа с динамическими величинами связана со ссылочным типом данных.
Величины, имеющие ссылочный тип, называют указателями.
Указатель содержит адрес поля в динамической памяти, хранящего величину определенного типа. Сам указатель располагается в статической памяти.
Статическая память Динамическая память
Указатель Величина
Адрес Значение
Адрес величины это номер первого байта памяти, в котором располагается величина.
Тип указателя определяется специальным символом "^", за которым следует идентификатор типа динамической переменной. К ней можно будет обращаться через переменную-указатель данного типа. Основными операциями, в которых участвуют указатели, являются:
присвоения, получения адреса, выборка или проверка на равенство или неравенство.
Примеры описаний указателей в паскале:
type <тип_указателя> = ^<имя_типа_динамической_переменной>
Например:
type uk=^integer;
ukc=^char; uks=^string[20];
При объявлении переменных-указателей резервируется только область памяти для
каждой из них ( по 2 слова или 4 байта на переменную.) Область памяти, куда будут помещены динамические переменные, еще не зарезервирована.
или
var x,y,z:uk; c1,c2:ukc; s1,s2:uks; или
var x,y,z:^integer;
c1,c2:^char; s1,s2:^string[20];
Type Massiv =Array[1..10] of integer;
Var p1: ^Integer;
P2:^Char;
Pm:^Massiv;
Здесь р1- указатель на динамическую величину целого типа;
Р2- указатель на динамическую величину символьного типа;
Pm- указатель на динамическую массив, тип которого задан в разделе Type.
Память под динамическую величину, связанную с указателем, выделяется в результате выполнения стандартной процедуры NEW.
Формат обращения к этой процедуре выглядит так:
NEW((<указатель>);
После этого создается динамическая величина вида
<имя динамической величины><указатель>^
В нашем примере
NEW(P1); NEW(P2); NEW(PM);
После их выполнения в динамической памяти выделяется место под 2 скалярные величины и 1 под массив.
В дальнейшем с этим переменными можно производить действия как с обычными переменными:
Р1^:=25;
P2^:=w;
For i:=1 to 10 do pm^[i]:=I;
Значение указателя может определяться оператором присваивания:
<указатель>:=<ссылочное выражения>;
В качестве ссылочное выражения можно использовать:
- указатель;
- ссылочную функцию(функцию, значением которой является указатель):
- константу NIL.
NIL- константа, обозначающая пустую ссылку.
Пример №1.
Var p,d: ^Integer;
P1:^Char;
Pm:^Massiv;
Тогда можно записать
D:=p; p1:=Nil;
Program pr1;
Var d,p:^integer;
Begin new(d); read(d^); write(d^); dispose(d);
New(p); read(p^); write(p^); dispose(p); p:=d; write(p^,d^) end.
В программе pr1 указатели d и p ссылаются на одну и ту же величину.
Пример №2
program pr1;
var d,p:^integer;
begin
new(d);read(d^);
writeln('d=^',d^);
new(p);read(p^);
writeln('p=^',p^); dispose(p);dispose(d);
p:=d;
writeln('p1=',p^,'d1=',d^)
end.
Пример №3
program pr3;
var x1,x2,x3:^integer;
begin
new(x1);new(x2);new(x3);
readln(x1^,x2^);
x3^:= x1^+x2^;
writeln('x3=',x3^); dispose(x1);dispose(x2); dispose(x3);
end.
Создать вещественный массив из 10000 чисел, заполнить его случайными числами в диапазоне от 0 до 1. Вычислить среднее значение массива. Очистить динамическую память. Создать целый массив размеров 10000, заполнить его случайными целыми числами в диапазоне от -100 до 100 и вычислить среднее значение.
program sr;
const NMax=10000;
type diapason=1..NMax;
masint=array[diapason] of integer;
masreal=array[diapason] of real;
var piint:^masint;
preal:^masreal;
I,midint:longint;
midreal:real;
begin
midreal:=0;
midint:=0;
randomize;
new(preal);
for i:=1 to nmax do
begin preal^[i]:=random;
midreal:=midreal+preal^[i]
end;
dispose(preal);
new(pint);
for i:=1 to nmax do
begin
pint^[i]:=random(200)-100;
midint:=midint+pint^[i]
end;
writeln(среднее целое равно:,midint div max);
writeln(среднее вещественное равно:,(midreal/nmax):10:6)
Для понимания работы с динамической памятью рассмотрим материал более подробно
Динамическая память -- это оперативная память ПК, предоставляемая программе при ее работе, за вычетом сегмента данных (64 Кбайт), стека (обычно 16 Кбайт) и собственно тела программы. Размер динамической памяти можно варьировать в широких пределах. По умолчанию этот размер определяется всей доступной памятью ПК и, как правило, составляет не менее 200...300 Кбайт.
АДРЕСА И УКАЗАТЕЛИ
Для адресации в пределах 1 Мбайта нужно 20 двоичных разрядов, которые получаются из двух шестнадцатиразрядных слов (сегмента и смещения) следующим образом: содержимое сегмента смещается влево на 4 разряда, освободившиеся правые разряды заполняются нулями, результат складывается с содержимым смещения.
Каждому сегменту соответствует непрерывная и отдельно адресуемая область памяти. Сегменты могут следовать в памяти один за другим без промежутков или с некоторым интервалом, или, наконец, перекрывать друг друга. С помощью указателей можно размещать в динамической памяти любой из известных в Турбо Паскале типов данных.
ОБЪЯВЛЕНИЕ УКАЗАТЕЛЕЙ
Типизированные указатели
Выше были приведены примеры. Запишем ещё раз
var
p1: ^integer;
р2: ^real;
type
PerconPointer = ^PerconRecord;
PerconRecord = record
Name: string;
Job: string;
Next: PerconPointer
end;
Обратите внимание: при объявлении типа PerconPointer мы сослались на PerconRecord, который предварительно в программе объявлен не был. Как уже отмечалось, в Турбо Паскале последовательно проводится в жизнь принцип, в соответствии с которым перед использованием какого-либо идентификатора он должен быть описан. Исключение сделано только для указателей, которые могут ссылаться на еще не объявленный тип данных. Это исключение сделано не случайно. Динамическая память дает возможность реализовать широко используемую в некоторых программах организацию данных в виде списков. Каждый элемент списка имеет в своем составе указатель на соседний элемент, что обеспечивает возможность просмотра и коррекции списка.
Списочная структура данных
Пояснения и примеры
1. Var p1: ^Integer;
P2:^Char;
2. Type Massiv =Array[1..10] of integer;
Var Pm:^Massiv;
Здесь р1- указатель на динамическую величину целого типа;
Р2- указатель на динамическую величину символьного типа;
Pm- указатель на динамический массив, тип которого задан в разделе Type.
3.
Type PersonPointer = ^PersonRecord;
PersonRecord = record
Name: String;
Job: String;
Next: PersonPointer;
end;
При объявлении указателя можно сослаться на тип, который ещё не описан. Это используется в объектно-ориентированной технологии.
При объявлении данных динамической структуры в разделе описания программы указывается не сама переменная какого-либо типа, а указатель (ссылка на неё). В результате указатель будет обычной переменной, а переменная, на которую он указывает, - динамической.
Нетипизированныый.указатель
В Турбо Паскале можно объявлять указатель и не связывать его при этом с каким-либо конкретным типом данных. например:
var
р: pointer;
Поскольку нетипизированные указатели не связаны с конкретным типом, с их помощью удобно динамически размещать данные, структура и тип которых меняются в ходе работы программы.
В Турбо Паскале можно передавать значения только между указателями, связанными с одним и тем же типом данных. Если, например,
var
p1,p2: ^integer;
р3: ^real;
рр: pointer;
то присвяивание
р1 := р2;
вполне допустимо, в то время как
р1 := р3;
запрещено, поскольку Р1 и Р3 указывают на разные типы данных. Это ограничение, однако, не распространяется на нетипизированные указатели, поэтому мы могли бы записать
pp := р3;
р1 := рр;
и тем самым достичь нужного результата.
Действия с указателем и организацию динамических структур данных принято для наглядности изображать графически, используя специальные обозначения. Принято следующие обозначения состояний переменной типа указатель:
P
1) ?
Указатель Р находится в неопределённом состоянии. Это происходит сразу после его объявления в программе или после освобождения той области памяти, на которую он указывает.
Р Р ^
2) адрес
Указатель содержит адрес какой-либо переменной, память под которую была предварительно выделена.
Р ^ - это область памяти , на которую указывает указатель Р, т.е. чтобы получить доступ к динамически выделенной памяти, необходимо обратиться к p^. При таком обращении становится доступным содержимое выделенной памяти, т.е. значение динамической переменной. Этой переменной можно уже непосредственно присваивать значения соответствующего типа, например : x^:=1 ; s1^:='таблица 1';
ВЫДЕЛЕНИЕ И ОСВОБОЖДЕНИЕ ДИНАМИЧЕСКОЙ ПАМЯТИ
Рассмотрим структуру и использование оперативной памяти компьютера в операционной системе MS DOS (основные принципы распределения памяти справедливы и для Windows):
Области самых младших и самых старших адресов занимают системные ресурсы, обеспечивающие функционирование операционной системы.
Сразу за системной областью младших адресов располагается исполняемый код прикладной программы.
Сегмент данных содержит ячейки для хранения значений глобальных констант и переменных.
Стек представляет собой буфер для временного хранения промежуточных (локальных) переменных и констант.
Перечисленные фрагменты ОЗУ составляют так называемую статическую память, которая заполняется сразу при запуске программы и не освобождается до ее завершения.
Следующий участок ОЗУ (по ходу возрастания адресов) представляет собой динамическую память, или кучу. Ячейки кучи могут неоднократно выделяться и освобождаться по мере необходимости во время выполнения программы, т.е. динамически.
В Паскале все константы и переменные, объявленные в разделе const и var основной программы или модуля, располагаются в сегменте данных, т.е. статической памяти. Размер сегмента данных и стека составляет соответственно 64 К и 16 К, что сильно ограничивает разработку программ, оперирующих большими массивами переменных емкого типа. Кардинальным решением этой проблемы является использование указателей и соответствующих средств доступа к динамической памяти.
Указатель, или ссылка это переменная, значением которой является физический адрес некоторой ячейки памяти (чаще всего динамической).
Адрес каждой ячейки памяти ПК занимает 4 байта, или два машинных слова. Первое слово задает смещение адреса относительно начала сегмента, а второе абсолютный адрес сегмента. Сегмент это участок памяти, имеющий длину 64К и начинающийся с физического адреса, кратного 16 (0, 16, 32, 48 и т.д.). Смещение показывает, сколько байтов от начала сегмента нужно пропустить, чтобы достичь нужного адреса. Обычно слова адреса записываются в шестнадцатиричной системе через двоеточие, например:
F3A1 : 128D
смещение сегмент
Пример
Пусть имеется описание:
type
TArr = array [1..20] of Real;
var
Pint: ^Integer;
Parr: ^TArr;
Замечание: Обычно идентификаторы типа указателя и переменной-указателя начинаются с заглавной буквы P.
Для приведенного описания:
Pint это переменная, указывающая на элемент целого типа (целое число);
Parr это указатель адреса первого элемента массива вещественных чисел.
Принципиальным является то, что сами указатели являются статическими переменными и располагаются в сегменте данных, а элементы, на которые ссылаются указатели, занимают место в куче.
Это иллюстрируется следующей схемой:
Стандартные функции для работы с указателями:
addr(x) : pointer возвращает адрес х (аналогично операции @), где х имя переменной или подпрограммы;
seg (x) : word возвращает адрес сегмента для х;
ofs (x) : word возвращает смещение для х;
cseg : word возвращает значение регистра сегмента кода CS;
dseg : word возвращает значение регистра сегмента данных DS;
ptr (seg, ofs : word) : pointer по заданному сегменту и смещению формирует адрес типа pointer.
|