Адрес объекта — различия между версиями
Ulysses (обсуждение | вклад) (Новая страница: «Как известно, переменная класса в PascalABC.NET — это ссылка, она хранит адрес объекта в динами…») |
Ulysses (обсуждение | вклад) |
||
Строка 1: | Строка 1: | ||
− | Как известно, переменная класса в PascalABC.NET — это ссылка, она хранит адрес объекта в динамической памяти. | + | Как известно, переменная класса в PascalABC.NET — это ссылка, она хранит адрес объекта в динамической памяти. Зададимся вопросом: |
− | # Получить адрес ссылки («адрес адреса») в переменную-бестиповый указатель ( | + | <blockquote> |
− | # Присвоить значение-адрес, хранимый в бестиповом указателе, переменной | + | Как узнать численное значение этого адреса? |
+ | </blockquote> | ||
+ | |||
+ | '''Вариант 1 (неверный).''' Обычная печать (<tt>write</tt>) классовой переменной тут не поможет (проверьте, что она делает). | ||
+ | |||
+ | '''Вариант 2 (неверный).''' Записать значение классовой переменной в переменную типа «указатель на объект класса». Логически эти типы эквивалентны, так что мысль вполне разумна. Однако в целях безопасности указатели на ссылочные типы (класс — это ссылочный тип) запрещены. | ||
+ | |||
+ | '''Вариант 2* (неверный).''' Записать значение классовой переменной в переменную типа «указатель на целое» (или любой размерный тип вместо целого). В конце концов, мы не собираемся разыменовывать этот указатель, а только хотим увидеть его значение (то есть адрес). Потому тип на который указывает такой указатель не существенен в этой задаче. Однако система типов Паскаля не даст выполнить присваивание между указателями и ссылками. | ||
+ | |||
+ | '''Вариант 3 (единственно верный).''' Если запрещены указатели на ссылочные типы и присваивания между ссылками и указателями, то можно попытаться получить «указатель на ссылку» и уже его привести к типу «указатель на указатель», а затем разыменовать, чтобы получить желанное значение исходной ссылки (то есть адрес объекта). Под «приведением типа» одного указателя к другому понимается трюк с использованием промежуточного бестипового указателя, который позволяет присваивать указатели на разные типы между собой. Итак, требуется выполнить последовательность из трёх шагов: | ||
+ | |||
+ | # Получить адрес ссылки («адрес адреса») в переменную-бестиповый указатель (то есть стереть память о том, что ссылка указывала на классовый объект) | ||
+ | # Присвоить значение-адрес, хранимый в бестиповом указателе, переменной типа «указатель на указатель на целое» (вместо целого может быть любой размерный тип). | ||
# Распечатать значение по адресу, хранимому в последней переменной — мы вернёмся к значению адреса объекта, но забыв его тип. | # Распечатать значение по адресу, хранимому в последней переменной — мы вернёмся к значению адреса объекта, но забыв его тип. | ||
Строка 11: | Строка 23: | ||
<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"> |
Версия 10:38, 9 марта 2016
Как известно, переменная класса в PascalABC.NET — это ссылка, она хранит адрес объекта в динамической памяти. Зададимся вопросом:
Как узнать численное значение этого адреса?
Вариант 1 (неверный). Обычная печать (write) классовой переменной тут не поможет (проверьте, что она делает).
Вариант 2 (неверный). Записать значение классовой переменной в переменную типа «указатель на объект класса». Логически эти типы эквивалентны, так что мысль вполне разумна. Однако в целях безопасности указатели на ссылочные типы (класс — это ссылочный тип) запрещены.
Вариант 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^);
Заключение
Такая последовательность действий может казаться переусложнённой. Хорошим мысленным упражнением может быть попытка сократить её. Если вам удастся сделать это, обсудите результат с преподавателем — он укажет на ошибку.