Есть ли платформы, где указатели на разные типы имеют разные размеры? - PullRequest
57 голосов
/ 27 мая 2009

Стандарт C позволяет указателям разных типов иметь разные размеры, например sizeof(char*) != sizeof(int*) разрешено. Однако он требует, чтобы, если указатель был преобразован в void*, а затем преобразован обратно в его исходный тип, он сравнивался как равный его первоначальному значению. Следовательно, логически следует, что sizeof(void*) >= sizeof(T*) для всех типов T, верно?

На большинстве распространенных платформ, используемых сегодня (x86, PPC, ARM и 64-битные варианты и т. Д.), Размер всех указателей равен собственному размеру регистра (4 или 8 байт), независимо от того, на что тип. Существуют ли какие-либо эзотерические или встроенные платформы, где указатели на разные типы могут иметь разные размеры? Я специально спрашиваю о data указателях, хотя мне также было бы интересно узнать, есть ли платформы, где указатели function имеют необычные размеры.

Я определенно не спрашиваю о функциях указателя на члены C ++ и функций указателя на член. Они имеют необычные размеры на общих платформах и могут даже варьироваться в пределах одной платформы в зависимости от свойств указателя на класс (неполиморфное, одиночное наследование, множественное наследование, виртуальное наследование или неполный тип).

Ответы [ 7 ]

45 голосов
/ 08 октября 2009

Ответ из C FAQ :

Серия Prime 50 использовала сегмент 07777, смещение 0 для нулевого указателя, по крайней мере для PL / I. Более поздние модели использовали сегмент 0 со смещением 0 для нулевых указателей в C, что потребовало новых инструкций, таких как TCNP (тестовый нулевой указатель C), очевидно, в качестве дополнения ко всему существующему плохо написанному коду C, который делал неверные предположения. Старые машины Prime с адресацией слов также были известны тем, что требовали указателей большего размера (char *), чем указатели слова (int *).

Серия Eclipse MV от Data General имеет три архитектурно поддерживаемых формата указателей (указатели слов, байтов и битов), два из которых используются компиляторами C: указатели байтов для char * и void * и указатели слов для всего остального , По историческим причинам во время эволюции 32-битной линии MV из 16-битной линии Nova указатели слов и указатели байтов имели биты смещения, косвенности и защиты кольца в разных местах слова. Передача несоответствующего формата указателя в функцию привела к сбоям защиты. В конце концов, компилятор MV C добавил множество опций совместимости, чтобы попытаться справиться с кодом, имеющим ошибки несоответствия типов указателей.

Некоторые мэйнфреймы Honeywell-Bull используют битовую комбинацию 06000 для (внутренних) нулевых указателей.

CDC Cyber ​​180 Series имеет 48-битные указатели, состоящие из кольца, сегмента и смещения. Большинство пользователей (в кольце 11) имеют нулевые указатели 0xB00000000000. На старых CDC-машинах с одним дополнением часто использовалось слово «все-один-бит» в качестве специального флага для всех типов данных, включая недопустимые адреса.

Старая серия HP 3000 использует другую схему адресации для байтовых адресов, чем для адресных слов; как и некоторые из машин, описанных выше, поэтому он использует различные представления для указателей char * и void *, чем для других указателей.

Symbolics Lisp Machine, помеченная архитектура, даже не имеет обычных числовых указателей; он использует пару (в основном, несуществующий дескриптор) в качестве нулевого указателя C.

В зависимости от используемой «модели памяти», процессоры семейства 8086 (ПК совместимы) могут использовать 16-битные указатели данных и 32-битные функции указатели или наоборот.

Некоторые 64-битные машины Cray представляют int * в младших 48 битах слово; char * дополнительно использует некоторые из старших 16 бит, чтобы указать адрес байта в слове.

Дополнительные ссылки: сообщение от Криса Торека с более подробной информацией. о некоторых из этих машин.

28 голосов
/ 27 мая 2009

Не совсем то, о чем вы спрашиваете, но в 16-битные времена DOS / Windows у вас было различие между указателем и дальним указателем, последний был 32-разрядным.

Возможно, у меня неправильный синтаксис ...

int *pInt = malloc(sizeof(int));
int far *fpInt = _fmalloc(sizeof(int));

printf("pInt: %d, fpInt: %d\n", sizeof(pInt), sizeof(fpInt));

Выход:

ПИНТ: 2, ФПИНТ 4

13 голосов
/ 27 мая 2009

Следовательно, логически следует, что sizeof(void*) >= sizeof(T*) для всех типов T, верно?

Это не обязательно следует, поскольку sizeof относится к представлению хранилища, и не все битовые комбинации должны быть допустимыми значениями. Я думаю, что вы могли бы написать соответствующую реализацию, где sizeof(int*) == 8, sizeof(void*) == 4, но для int * не более 2 ^ 32 возможных значений. Не знаю, почему вы хотите.

11 голосов
/ 27 мая 2009

В золотые годы DOS, 8088-х и сегментированной памяти было принято указывать «модель памяти», в которой, например, весь код помещается в 64 КБ (один сегмент), но данные могут занимать несколько сегментов; это означало, что указатель функции будет 2 байта, указатель данных - 4 байта. Не уверен, что кто-то еще программирует для машин такого типа, может быть, некоторые все еще выживают во встроенных приложениях.

7 голосов
/ 27 мая 2009

Ближние и дальние указатели все еще используются в некоторых встроенных микроконтроллерах с выгружаемой флэш-памятью или оперативной памятью, чтобы позволить вам указывать на данные на той же странице (ближний указатель) или другой странице (дальний указатель, который больше, потому что включает в себя страницу информация).

Например, микроконтроллер HCS12 Freescale использует 16-битную архитектуру фон Неймана, что означает, что ни один адрес не может быть больше 16 бит. Из-за ограничения на объем доступного пространства кода существует 8-разрядный регистр страниц.

Таким образом, чтобы указать на данные в той же кодовой странице, вы просто указываете 16-битный адрес; это ближний указатель.

Чтобы указать на данные в другой кодовой странице, вы должны включить в эту страницу и 8-битный номер страницы, и 16-битный адрес, что приведет к 24-битному дальнему указателю.

7 голосов
/ 27 мая 2009

Можно легко представить машину архитектуры Гарварда, имеющую разные размеры для указателей функций и всех других указателей. Не знаю примера ...

6 голосов
/ 10 марта 2010

Возможно, что размер указателей на данные отличается, например, от указателей на функции. Обычно это происходит в микропроцессоре для встроенной системы. Машины с гарвардской архитектурой, такие как упомянутые dmckee, делают это простым.

Оказывается, это заставляет gcc бэкендов развиваться! :)

Редактировать: я не могу вдаваться в детали конкретной машины, о которой я говорю, но позвольте мне добавить, почему машины Гарварда делают это таким простым. Гарвардская архитектура имеет другое хранилище и пути к инструкциям и данным, поэтому, если шина для инструкций «больше», чем шина для данных, вы обязательно должны иметь указатель на функцию, размер которого больше, чем указатель на данные!

...