КРАТКОЕ РЕЗЮМЕ
(которое я также поставлю вверху):
(0) Думать об указателях как адресах часто является хорошим инструментом обучения и часто является реальной реализацией указателей на обычные типы данных.
(1) Но на многих, возможно, большинстве, указатели компиляторов на функции не являются адресами, а больше, чем адрес (как правило, в 2 раза, иногда больше), или на самом деле являются указателями на структуру в памяти, которая содержит адреса функций и тому подобное постоянный пул.
(2) Указатели на элементы данных и указатели на методы часто даже более странны.
(3) Устаревший код x86 с проблемами указателей FAR и NEAR
(4) Несколько примеров, особенно IBM AS / 400, с безопасными «жирными указателями».
Я уверен, что вы можете найти больше.
ДЕТАЛЬ:
UMMPPHHH !!!!! Многие из ответов до сих пор являются довольно типичными ответами "программиста", но не "компилятора" или "аппаратного обеспечения". Так как я притворяюсь аппаратным и часто работаю с компилятором, позвольте мне добавить два моих цента:
На многих, возможно, на большинстве компиляторов Си указатель на данные типа T
является, по сути, адресом T
.
Хорошо.
Но даже на многих из этих компиляторов некоторые указатели НЕ являются адресами. Вы можете сказать это, посмотрев на sizeof(ThePointer)
.
Например, указатели на функции иногда намного больше, чем обычные адреса. Или они могут включать уровень косвенности. Эта статьяпредоставляет одно описание, включающее процессор Intel Itanium, но я видел другие. Как правило, для вызова функции вы должны знать не только адрес кода функции, но также адрес пула констант функции - область памяти, из которой константы загружаются с помощью одной инструкции загрузки, а не компилятор, который должен генерировать 64-битная константа из нескольких команд Load Immediate и Shift и OR. Таким образом, вместо одного 64-битного адреса, вам нужно 2 64-битных адреса. Некоторые ABI (двоичные интерфейсы приложений) перемещают это как 128 бит, тогда как другие используют уровень косвенности, причем указатель функции фактически является адресом дескриптора функции, который содержит 2 фактических адреса, которые только что упомянуты. Что лучше? Зависит от вашей точки зрения: производительность, размер кода, и некоторые проблемы совместимости - часто код предполагает, что указатель может быть приведен к длинному или длинному длинному, но может также предполагать, что длинный длинный составляет ровно 64 бита. Такой код может не соответствовать стандартам, но, тем не менее, клиенты могут захотеть, чтобы он работал.
У многих из нас остались болезненные воспоминания о старой сегментированной архитектуре Intel x86 с БЛИЖАЙШИМИ И ДАЛЬНЕЙШИМИ указателями. К счастью, они почти вымерли, так что только краткое резюме: в 16-битном реальном режиме реальный линейный адрес был
LinearAddress = SegmentRegister[SegNum].base << 4 + Offset
В то время как в защищенном режиме это может быть
LinearAddress = SegmentRegister[SegNum].base + offset
с результирующим адресом, проверяемым по ограничению, установленному в сегменте. Некоторые программы использовали не совсем стандартные объявления указателей FAR и NEAR в C / C ++, но многие только что сказали *T
- но были переключатели компилятора и компоновщика, так что, например, указатели кода могли быть рядом с указателями, просто 32-битное смещение относительно того, что находится в регистр CS (кодовый сегмент), в то время как указатели данных могут быть указателями FAR, указывая как 16-битный номер сегмента, так и 32-битное смещение для 48-битного значения. Теперь, оба эти количества, безусловно, связаны с адресом, но, поскольку они не одинакового размера, какой из них является адресом? Кроме того, сегменты также имели разрешения - только чтение, чтение-запись, исполняемый файл - в дополнение к материалам, связанным с фактическим адресом.
Более интересным примером, IMHO, является (или, возможно, был) семейство IBM AS / 400. Этот компьютер был одним из первых, кто внедрил ОС на C ++. Указатели на этом механизме обычно в 2 раза превышают реальный размер адреса - например, в этой презентацииговорит, 128-битные указатели, но фактические адреса были 48-64 бит, и, опять же, некоторая дополнительная информация, которая называется возможностью, которая предоставляла разрешения, такие как чтение, запись, а также ограничение для предотвращения переполнения буфера. Да, вы можете сделать это совместимо с C / C ++ - и если бы это было повсеместно, китайская PLA и славянская мафия не взломали бы так много западных компьютерных систем. Но исторически большинство программирования на C / C ++ пренебрегали безопасностью для производительности. Самое интересное, что семейство AS400 позволило операционной системе создавать безопасные указатели, которые можно было бы дать непривилегированному коду, но которые непривилегированный код не мог подделать или подделать. Опять же, безопасность и, в то время как совместимый со стандартами, много неаккуратного, не отвечающего стандартам кода C / C ++ не будут работать в такой защищенной системе. Опять же, есть официальные стандарты,
Теперь я избавлюсь от своего мыла безопасности и упомяну о некоторых других способах, которыми указатели (различных типов) часто не адресованы в действительности: указатели на элементы данных, указатели на методы функций-членов и их статические версии больше, чем обычный адрес. Как говорится в этом посте :
Есть много способов решения этой проблемы [проблемы, связанные с единичным или множественным наследованием и виртуальным наследованием]. Вот как компилятор Visual Studio решает справиться с этим: указатель на функцию-член класса с множественным наследованием - это действительно структура. «И они продолжают говорить:« Приведение указателя на функцию может изменить его размер! ».
Как вы, вероятно, догадываетесь из моих понтификаций по (в) безопасности, я принимал участие в аппаратных / программных проектах на C / C ++, где указатель рассматривался скорее как возможность, чем необработанный адрес.
Я мог бы продолжить, но я надеюсь, что вы поняли идею.
КРАТКОЕ РЕЗЮМЕ
(которое я также поставлю вверху):
(0) представление об указателях как об адресах часто является хорошим средством обучения и часто является реальной реализацией указателей на обычные типы данных.
(1) Но на многих, возможно, большинстве, указатели компиляторов на функции не являются адресами, но больше, чем адрес (обычно в 2 раза, иногда больше), или на самом деле являются указателями на структуру в памяти, которая содержит адреса функций и тому подобное постоянный пул.
(2) Указатели на элементы данных и указатели на методы часто даже более странны.
(3) Устаревший код x86 с проблемами указателей FAR и NEAR
(4) Несколько примеров, особенно IBM AS / 400, с безопасными «жирными указателями».
Я уверен, что вы можете найти больше.