Адрес объекта — различия между версиями

Материал из Вики ИТ мехмата ЮФУ
Перейти к: навигация, поиск
(Новая страница: «Как известно, переменная класса в PascalABC.NET — это ссылка, она хранит адрес объекта в динами…»)
 
 
(не показаны 3 промежуточные версии этого же участника)
Строка 1: Строка 1:
Как известно, переменная класса в PascalABC.NET — это ссылка, она хранит адрес объекта в динамической памяти. В целях безопасности указатели на ссылочные типы (класс — это ссылочный тип) запрещены, поэтому до содержимого объекта через указатель мы добраться не сможем. А вот узнать его адрес можно. Это делается с помощью последовательности трёх шагов:
+
Примечание. В этой статье предполагается беглое знакомство с темой «[http://it.mmcs.sfedu.ru/wiki/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F_%E2%80%94_%D0%B2%D1%82%D0%BE%D1%80%D0%BE%D0%B9_%D1%81%D0%B5%D0%BC%D0%B5%D1%81%D1%82%D1%80_08-09;_%D0%9C%D0%B8%D1%85%D0%B0%D0%BB%D0%BA%D0%BE%D0%B2%D0%B8%D1%87_%D0%A1.%D0%A1.;_II_%D1%87%D0%B0%D1%81%D1%82%D1%8C Уазатели]».
  
# Получить адрес ссылки («адрес адреса») в переменную-бестиповый указатель (следует стереть память о том, что ссылка указывала на классовый объект)
+
 
# Присвоить значение-адрес, хранимый в бестиповом указателе, переменной тип «указатель на указатель на целое» (вместо целого может быть размерный любой тип).
+
Как известно, переменная класса в PascalABC.NET — это ссылка, она хранит адрес объекта в динамической памяти. Зададимся вопросом:
 +
 
 +
<blockquote>
 +
Как узнать численное значение этого адреса?
 +
</blockquote>
 +
 
 +
'''Вариант 1 (неверный).''' Обычная печать (<tt>write</tt>) классовой переменной тут не поможет (проверьте, что она делает).
 +
 
 +
'''Вариант 2 (неверный).''' Непосредственная работа с адресами в Паскале происходит через типы указателей. Логичное желание: записать значение классовой переменной в переменную типа «указатель на объект класса». Однако тип укахзателя на объект в Паскале отсутсвует.
 +
 
 +
'''Вариант 2* (неверный).''' Записать значение классовой переменной в переменную типа «указатель на целое» (или любой размерный тип вместо целого). В конце концов, мы не собираемся разыменовывать этот указатель, а только хотим увидеть его значение (то есть адрес). Потому тип на который указывает такой указатель не существенен в этой задаче. Однако система типов Паскаля не даст выполнить присваивание между указателями и ссылками.
 +
 
 +
'''Вариант 3 (единственно верный).''' Если запрещены указатели на ссылочные типы и присваивания между ссылками и указателями, то можно попытаться получить «указатель на ссылку» и уже его привести к типу «указатель на указатель», а затем разыменовать, чтобы получить желанное значение исходной ссылки (то есть адрес объекта). Под «приведением типа» одного указателя к другому понимается трюк с использованием промежуточного бестипового указателя, который позволяет присваивать указатели на разные типы между собой. Итак, требуется выполнить последовательность из трёх шагов:
 +
 
 +
# Получить адрес ссылки («адрес адреса») в переменную-бестиповый указатель (то есть стереть память о том, что ссылка указывала на классовый объект)
 +
# Присвоить значение-адрес, хранимый в бестиповом указателе, переменной типа «указатель на указатель на целое» (вместо целого может быть любой размерный тип).
 
# Распечатать значение по адресу, хранимому в последней переменной — мы вернёмся к значению адреса объекта, но забыв его тип.
 
# Распечатать значение по адресу, хранимому в последней переменной — мы вернёмся к значению адреса объекта, но забыв его тип.
  
Строка 9: Строка 24:
 
Пусть имеется класс <tt>MyClass</tt>. Опишем тип <tt>PPInteger</tt> «указатель на указатель-на-целое»: вместо «целое» можно выбрать любой размерный тип, зато тип <tt>PInteger</tt> уже есть среди стандартных.
 
Пусть имеется класс <tt>MyClass</tt>. Опишем тип <tt>PPInteger</tt> «указатель на указатель-на-целое»: вместо «целое» можно выбрать любой размерный тип, зато тип <tt>PInteger</tt> уже есть среди стандартных.
  
<source lang="pascal">type PPInteger = PInteger^;</source>
+
<source lang="pascal">type PPInteger = ^PInteger;</source>
  
Сохраним в переменную типа <tt>PPInteger</tt> адрес переменной класса: напрямую это сделать нельзя, но можно, как было указано выше, использовать промежуточную переменную-бестиповой указатель.
+
Сохраним в переменную типа <tt>PPInteger</tt> адрес переменной класса: напрямую это сделать нельзя, но можно, как было указано выше, использовать промежуточную переменную-бестиповый указатель.
  
 
<source lang="pascal">
 
<source lang="pascal">

Текущая версия на 12:25, 9 марта 2016

Примечание. В этой статье предполагается беглое знакомство с темой «Уазатели».


Как известно, переменная класса в PascalABC.NET — это ссылка, она хранит адрес объекта в динамической памяти. Зададимся вопросом:

Как узнать численное значение этого адреса?

Вариант 1 (неверный). Обычная печать (write) классовой переменной тут не поможет (проверьте, что она делает).

Вариант 2 (неверный). Непосредственная работа с адресами в Паскале происходит через типы указателей. Логичное желание: записать значение классовой переменной в переменную типа «указатель на объект класса». Однако тип укахзателя на объект в Паскале отсутсвует.

Вариант 2* (неверный). Записать значение классовой переменной в переменную типа «указатель на целое» (или любой размерный тип вместо целого). В конце концов, мы не собираемся разыменовывать этот указатель, а только хотим увидеть его значение (то есть адрес). Потому тип на который указывает такой указатель не существенен в этой задаче. Однако система типов Паскаля не даст выполнить присваивание между указателями и ссылками.

Вариант 3 (единственно верный). Если запрещены указатели на ссылочные типы и присваивания между ссылками и указателями, то можно попытаться получить «указатель на ссылку» и уже его привести к типу «указатель на указатель», а затем разыменовать, чтобы получить желанное значение исходной ссылки (то есть адрес объекта). Под «приведением типа» одного указателя к другому понимается трюк с использованием промежуточного бестипового указателя, который позволяет присваивать указатели на разные типы между собой. Итак, требуется выполнить последовательность из трёх шагов:

  1. Получить адрес ссылки («адрес адреса») в переменную-бестиповый указатель (то есть стереть память о том, что ссылка указывала на классовый объект)
  2. Присвоить значение-адрес, хранимый в бестиповом указателе, переменной типа «указатель на указатель на целое» (вместо целого может быть любой размерный тип).
  3. Распечатать значение по адресу, хранимому в последней переменной — мы вернёмся к значению адреса объекта, но забыв его тип.

Выполним эту последовательность.

Пусть имеется класс MyClass. Опишем тип PPInteger «указатель на указатель-на-целое»: вместо «целое» можно выбрать любой размерный тип, зато тип PInteger уже есть среди стандартных.

type PPInteger = ^PInteger;

Сохраним в переменную типа PPInteger адрес переменной класса: напрямую это сделать нельзя, но можно, как было указано выше, использовать промежуточную переменную-бестиповый указатель.

var c: MyClass := new MyClass;
var p: pointer := @c;
var ppi: PPInteger := p;

Распечатаем адрес объекта, спрятанный в переменной PPInteger, то есть значение по адресу, хранимому в последней переменной.

writeln(ppi^);

Заключение

Такая последовательность действий может казаться переусложнённой. Хорошим мысленным упражнением может быть попытка сократить её. Если вам удастся сделать это, обсудите результат с преподавателем — он укажет на ошибку.