Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Объявление и инициализация указателей




Рассмотрим процесс объявления указателей. Компьютеру необходимо отслеживать тип значения, на которое ссылается указатель. Например, адрес переменной типа char выглядит так же, как и адрес переменной типа real, но для типов char и real используется различное количество байтов и различные внутренние форматы для хранения значений. Поэтому при объявлении указателя необходимо точно определять, на какой тип данных он указывает.

Последний пример содержит следующее объявление:

p_updates: ^Integer;

Это выражение указывает, что комбинация p_updates^ принадлежит к типу Integer. Поскольку операция ^ выполняется по отношению к указателю, переменная p_updates сама должна быть указателем. Мы говорим, что p_updates указывает на значение типа Integer. Мы также говорим, что типом для p_updates должен быть указатель на Integer. Повторим: p_updates является указателем (адресом), a p_updates^ — переменной типа Integer, но не указателем.

Для объявления указателей на другие типы переменных используется аналогичный синтаксис:

tax_ptr: ^Real; //tax_ptr указывает на тип Real

str: ^Char; //str указывает на тип Char

Поскольку tax_ptr объявляется как указатель на тип Real, компилятор определяет, что значение tax_ptr^ принадлежит к типу Real. Другими словами, он распознает, что tax_ptr^ представляет собой величину, которая хранится в формате с плавающей точкой и занимает (в большинстве систем) восемь байтов. Переменная-указатель никогда не бывает просто указателем. Она всегда указывает на определенный тип данных. Так, переменная tax_ptr имеет тип "указатель на тип Real ", a str — "указатель на тип char ". Несмотря на то, что обе переменные являются указателями, они указывают на два различных типа данных. Подобно массивам, указатели являются производными от других типов данных.

Заметим, что в то время, как tax_ptr и str указывают на типы данных, имеющие различную размерность, сами по себе две переменные — tax_ptr и str — имеют одинаковый размер. Иными словами, адрес переменной типа char имеет тот же размер, что и адрес переменной типа Real (точно так же, как номер дома 1016 может означать адрес большого универмага, а 1024 — адрес маленького коттеджа). По размеру или значению адреса ничего нельзя сказать о размере или типе переменной, так же, как и по номеру дома — о здании, которое находится по этому адресу. Обычно для хранения адреса требуются два или четыре байта, в зависимости от компьютера (в некоторых системах могут использоваться более длинные адреса или адреса различной длины для различных типов данных).

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

Var

higgens: Integer = 5;

pt: ^Integer = @higgens;

присваивают указателю pt адрес переменной higgens.

Программа 3_6

// инициализация указателя

program Project1;

 

{$APPTYPE CONSOLE}

 

Uses

SysUtils;

Var

higgens: Integer = 5;

pi: ^Integer = @higgens;

Begin

Writeln('Value of higgens = ', higgens, '; Address of higgens = ',

Integer(@higgens));

Writeln('Value of pi^ = ', pi^, '; Address of pi = ',

Integer(pi));

Readln;

end.

Результат:

Value of higgens = 5; Address of higgens = 4237700

Value of pi^ = 5; Address of pi = 4237700

Можно видеть, что программа присваивает адрес переменной higgens указателю pi, но не значению pi^.

Отметим однако, что неосторожное использование указателей может привести к нежелательным последствиям. Крайне важно не забывать о том, что при создании указателя компьютер выделяет память для хранения адреса, но не для хранения данных, на которые указывает этот адрес. Чтобы выделить места для данных, требуется дополнительное действие. Если вы такое действие не выполните (как в приведенном ниже примере), то окажетесь на прямом пути к краху:

Var

fellow: ^Integer; //создание указателя на переменную типа Integer

Begin

fellow^:= 5; //помещение значения по неопределенному адресу

Конечно, переменная fellow является указателем. Но на что она указывает? Программа не присвоила ей адрес. Куда же будет помещено значение 5? Мы не в состоянии ответить на этот вопрос. Поскольку переменная fellow не была инициализирована, она может принять любое значение. Какое бы значение она ни имела, программа будет интерпретировать его как адрес, по которому нужно сохранить число 5. Если fellow имеет значение 1200, то компьютер попытается поместить данные по адресу 1200, даже если окажется, что этот адрес находится в середине кода программы. Понятно одно: куда бы ни указывал указатель fellow, это не то место, куда вы намерены поместить число 5. Ошибки подобного рода коварны. Кроме того, их сложно выявлять.

Золотое правило для указателей: ВСЕГДА присваивайте указателю определенное и правильное значение адреса до применения к нему операции разыменования (^).

Выделение памяти с помощью оператора New

Теперь, когда вы имеете некоторое представление о том, как работают указатели, давайте посмотрим, как они могут реализовать такую важную технологию ООП, как распределение памяти во время выполнения программы. До сих пор мы присваивали указателям адреса переменных. Переменная является именованной областью памяти, которая резервируется во время компиляции, а указатели всего лишь служат псевдонимами для областей памяти, к которым можно в любом случае обратиться напрямую по имени. Истинная ценность указателей проявляется тогда, когда для хранения данных выделяется неименованная область памяти во время выполнения программы. В этом случае указатели становятся единственным способом доступа к этим областям памяти. Для этого существует оператор new.

Попробуем использовать эту новую технологию — создадим неименованное хранилище значений типа
Integer во время исполнения программы и будем обращаться к этим значениям с помощью указателя. Здесь ключевое значение имеет оператор new. Мы указываем оператору new, для какого типа данных выделяется память. Оператор new ищет блок памяти нужного размера и возвращает адрес этого блока. Присваивая этот адрес указателю, мы получаем желаемое. Ниже приводится пример реализации описанной технологии:

Var

pi: ^Integer;

Begin

New(pi);

Выражение New(pi) сообщает программе о том, что необходимо некоторое новое хранилище, подходящее для размещения значения типа Integer. Оператор new анализирует тип для вычисления необходимого количества байтов. Затем он отыскивает область памяти и возвращает адрес. Далее присваиваем адрес переменной pi, которая объявляется как указатель на тип Integer. Теперь рi является адресом, а рi^ — значением, хранящимся по этому адресу. Сравним это с присваиванием указателю адреса переменной:

Var

pt: ^Integer;

higgens: Integer;

Begin

pi:= @higgens;

В обоих примерах (рi и pt) указателю присваивается адрес переменной типа Integer. Во втором случае можно также обращаться к переменной типа Integer по имени: higgens. В первом же случае обращение возможно исключительно через указатель. Возникает вопрос: так как область памяти, на которую указывает рi, не имеет имени, как же к ней обращаться? Мы говорим, что рi указывает на объект данных. Но это не "объект" в смысле "объектно-ориентированного программирования"; это всего лишь "объект" в смысле "предмет". "Объект данных", под которым понимается любой блок памяти, выделенный для хранения элементарной группы данных, является более общим понятием, чем "переменная". Поэтому переменная также является объектом данных, но память, на которую указывает рi, не является переменной. Способ управления объектами данных с помощью указателей может показаться на первый взгляд довольно неудобным, однако он обеспечивает большие возможности по управлению организацией памяти в программе.

Программа 3_7

// использование оператора new

program Project1;

 

{$APPTYPE CONSOLE}

 

Uses

SysUtils;

Var

pt: ^Integer;

pd: ^Real;

 

Begin

New(pt); //выделение области памяти для значений типа Integer

pt^:= 1001; //место хранения значения

Writeln('Integer value = ', pt^, ': location = ', Integer(pt));

New(pd); //выделение области памяти для Real

pd^:= 10000001.0; //место хранения значения типа Real

Writeln('Real value = ', pd^:1:1, ': location = ', Integer(pd));

Write('size of pt = ', SizeOf(pt));

Writeln(': size of pt^ = ', SizeOf(pt^));

Write('size of pd = ', SizeOf(pd));

Writeln(': size of pd^ = ', SizeOf(pd^));

Dispose(pt); //освобождаем память

Dispose(pd);

 

Readln;

end.

Результат:

Integer value = 1001: location = 10571360

Real value = 10000001.0: location = 10571376

size of pt = 4: size of pt^ = 4

size of pd = 4: size of pd^ = 8

Примечания к программе

Программа использует оператор new для выделения памяти объектам данных типа Integer и Real. Это происходит при выполнении программы. Указатели pt и pd указывают на эти два объекта данных. Без них невозможно обращаться к выделенным участкам памяти. Благодаря указателям, можно использовать выражения pt^ и pd^ просто как переменные. Значения выражениям pt^ и pd^, присваиваются для того, чтобы присвоить значения новым объектам данных. Аналогично, выражения pt^ и pd^ используются для отображения этих значений с помощью оператора WriteLn.

Программа также демонстрирует одну из причин, по которой необходимо объявлять тип данных, на которые указывает указатель. Адрес сам по себе определяет только начало области памяти, в которой хранится объект, но не его тип или занимаемый размер. Взгляните на адреса двух значений из нашего примера. Они — всего лишь числа без обозначения типа или размера. К тому же заметим, что размерность указателя на тип Integer совпадает с размерностью указателя на тип Real: оба являются просто адресами. Но поскольку в программе объявлены типы указателей, программа знает, что значение pd^ относится к типу Real с размерностью восемь байтов, в то время как значение pt^ относится к типу Integer размерностью четыре байта. Когда программа выводит значение pd^, оператор WriteLn определяет, сколько байтов нужно прочесть и как их представить.

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...