Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
!!!ГОСы информатика и вычислительная техника.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
3.93 Mб
Скачать
  1. Распределение памяти при выполнении программ. Строчные переменные в языке Турбо Паскаль. Примеры.

Турбо Паскаль, разработанный фирмой Borland, работает под управлением MS–DOS. В Турбо Паскале нет специальных средств, поддерживающих работу с дополнительной памятью, поэтому адресное пространство компьютера составляет 1 Мбайт

При запуске программы (EXE–файла) MS–DOS организует в памяти нечто вроде анкеты на этот файл длиной 256 байт, которая называется префиксом структуры программы. После префикса начинается код EXE–файла. Код ЕХЕ–файла состоит из рабочего кода системного модуля, рабочих кодов подклю­чаемых модулей и рабочего кода основного блока программы. Статические глобальные переменные основного блока и все типизированные константы, включая локальные, располагаются в сегменте данных, общий объем которого не может в сумме превышать 64К. За сегментом данных следует область стека. В ней располагаются локальные переменные и параметры-значения процедур и функций во время их работы по вызову. Область стека не может превышать 64К (обычно 16К). Стек заполняется от своей верхней границы (она может быть назначена директивой компилятору $М) по направлению к началу, т.е. к старту сегмента. Выше стека программа отводит себе память под буфер для работы оверлеев – перекрывающихся частей программ. Если они не используются, то буфер не отводится. Еще выше располагается область памяти для размещения динамических переменных и структур данных, называ­емая областью кучи или просто кучей (еще она называется Heap‑областью).

По мере того, как программы становятся более сложными и требуется работа с большим количеством данных, область объемом в 64К, зарезерви­рованная в Турбо Паскале для данных, может оказаться недостаточной, чтобы содержать все необходимые программе данные.

Предположим, есть программа, требующая массива в 400 строк по 100 символов каждая. Для этого массива требуется примерно 40К, что меньше максимума в 64К. Если остальные переменные помещаются в оставшиеся 24К, массив такого объема проблемы не представляет. Но если нужно два таких массива? Это потребует 80К. Чтобы работать с большими объемами данных, нужно использовать динамически распределяемую область памяти.

Динамически распределяемая область памяти – это вся память, которую операционная система делает доступной для программы и которая не используется ее кодом, сегментом данных и стеком. Объемом распреде­ляемой динамической памяти можно управлять с помощью директивы компилятора $M.

Известно, что все переменные, встречающиеся в программе, должны быть описаны. Перед началом выполнения программы каждой переменной для размещения ее значений выделяется место в сегменте данных. Размер выделяемого места зависит от типа переменной. Например, для переменной типа Integer выделяется 2 байта. Обращение в программе к объекту, размещенному в некотором месте памяти, осуществляется с помощью имени переменной. Соответствие между переменной и сопоставленным ей местом в памяти сохраняется для описанных в программе переменных на всем протяжении выполнения программы. В Паскале имеются средства, позволяю­щие заниматься отведением и освобождением памяти для размещения объектов того или иного типа непосредственно по ходу выполнения програм­мы. Память в этом случае отводится в динамической области. Данные, размер которых задается непосредственно во время выполнения программы, называются динамическими. Для объявления динамических данных в Паскале используется ссылочный тип, называемый еще типом-указателем. С помощью ссылочного типа можно объявлять переменные, значением которых будет адрес ячейки памяти.

Ссылочные переменные

Описание ссылочного типа выглядит следующим образом:

Type

Ptr = ^t,

где t – стандартный или заранее описанный тип данных, называемый базовым типом. Сами адреса будут храниться в ссылочных переменных, которые описываются обычным образом, например, Var P : Ptr. Такие переменные для хранения адресов динамической памяти называются ссылками или указателями.

Пример.

Type

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.

Адрес хранится как два слова: одно из них определяет сегмент, а другое – смещение. Значение указателя не может быть в явном виде выведено на экран или печать. Его надо предварительно расшифровать

Так как значение указателя состоит из двух слов (Word), хранящих сегмент и смещение, можно вывести их в отдельности, используя функции Seg и Ofs:

Writeln(‘Сегмент ’, Seg(p), ‘ смещение ‘, Ofs(p));

Указатели могут обмениваться значениями через оператор присваивания. Типизированному указателю можно присвоить значение либо указателя того же типа, что и он сам, либо нетипизированного указателя. Если, например,

Var P1, P2 : ^Integer;

P3 : ^Real;

PP : Pointer;

то присваивание P1 := P2 вполне допустимо, в то время как P1 := P3 запрещено, поскольку P1 и P3 указывают на разные типы данных. Это ограничение не распространяется на нетипизированные указатели. Можно записать PP := P3; P1 := PP и достичь нужного результата. Присутствие в программе таких переприсвоений говорит о том, что программист делает это осознано и в программе действительно нужны такие действия. Указателю можно присвоить значение Nil. Nil – это предопределенная константа типа Pointer, соответствующая адресу 0000:0000 (пустая ссылка). Если указателю присвоено значение Nil, то этот указатель ни на какие данные не ссылается.

Указатели могут сравниваться с помощью операций отношения = или <> (не равно). Сравнение для указателей – ненадежная операция. Если два указателя содержат один и тот же адрес в памяти, но записанный в них разными способами, то они считаются различными. Зато можно проверить, ссылается ли указатель р на что–нибудь или нет путем сравнения p <> Nil.

Содержимое ячейки доступно через имя указателя. Чтобы обратиться к данным, находящимся по адресу, содержащемуся в указателе, используется символ “^”, который ставится сразу после имени ссылочной переменной. Эта операция называется операцией разыменования. Суть ее состоит в переходе от ссылочной переменной к значению, на которое она указывает. Пусть имеется следующее описание:

Var a, b: ^Real;

тогда в программе с переменными а^ и b^ допустимы все действия, что и с любыми переменными типа Real, например,

a^ := b^;

b^ := sin(a^); и т.д.

Память под любую динамически размещаемую переменную выделяется процедурой New(p). Только после выполнения процедуры New имеет смысл обращаться к ссылочным переменным. Параметром обращения к этой процедуре является типизированный указатель. В результате обращения указатель приобретает значение, соответствующее адресу, начиная с которого, можно разместить данные. Это адрес динамической области данных или кучи. Начало кучи хранится в стандартной переменной Heaporg, конец – в переменной Heapend. Текущую границу незанятой динамической памяти указывает указатель Heapptr. Если Var i^ integer, то после выполнения New(i) указатель i приобретет значение, которое перед этим имел указатель кучи Heapptr, а сам Heapptr увеличит свое значение на 2, так как длина внутреннего представления типа Integer, с которым связан указатель i, составляет 2 байта.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]