2009-05-27 6 views
52

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

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

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

+0

Любопытно, какой раздел стандарта позволяет использовать разные размеры указателя? Не могли бы вы опубликовать этот раздел? – JaredPar

+1

Nit-pick: «родной интегральный тип» в C должен быть int, что редко бывает 64-разрядным даже на 64-битных платформах AFAIK. Другими словами, LP64 более распространен, чем ILP64. – unwind

+0

@ JaredPar: Я не совсем уверен, где это говорится в стандарте, но на этой странице http://www.lysator.liu.se/c/rat/d9.html#4-9-6-1 упоминается его в отношении спецификатора формата% p fprintf. @unwind: s/собственный целочисленный размер/собственный размер регистра/ –

ответ

44

Answer from the C FAQ:

Серия Prime 50 используется сегмент 07777, смещение 0 для нулевого указателя, по крайней мере, для PL/I. В более поздних моделях использовался сегмент 0, смещение 0 для нулевых указателей на C, что требовало новых инструкций, таких как TCNP (Test C Null Pointer), очевидно, как сосок для всего сохранившегося плохо написанного кода на 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, тегированная архитектура, даже не имеет обычных числовых указателей; он использует пару (в основном несуществующий дескриптор) как С-нулевой указатель.

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

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

Дополнительные ссылки: A message from Chris Torek с более подробной информацией о некоторых из этих машин.

+2

+1 Почему это не принятый ответ? .. – dcow

+0

@ Давид Кауден Подозреваемый этот ответ был написан через 4 месяца после принятого ответа. Поскольку ОП попросил пример и получил его, этот ответ был принят. Этот пост, безусловно, более полный, заслуживает высокой оценки голосов. Возможно, высоко оцененные непринятые ответы заслуживают того, чтобы сообщество «принимало переопределение»? Однако это звучит как «мета». – chux

+0

@chux, вероятно, лучше просто сообщить об этом автору, что ответ на их вопрос значительно превзошел принятый ответ и позволит им пересмотреть. В конечном счете это зависит от того, чтобы задать вопрос, какой ответ лучше всего отвечает на их вопрос. Я просто прокомментировал, чтобы подчеркнуть тот факт, что я очень * очень наслаждаюсь этим ответом (= – dcow

11

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

+0

Они не так уж необычны во встроенном мире. DOS все еще используется много. –

+1

@ Нильс, я недавно (после того, как я опубликовал это сообщение) взял интервью у нового града (EE) и его основной опыт сборки (из встроенных приложений, конечно) оказался с Intel 8051 и Freescale 6811 - 8-бит потомки процессоров, которые я изучал в колледже в 70-е годы (!), и даже тогда мы заинтересовались более мощными, такими как Zilog Z80. Таким образом, 8088 и DOS были бы большим шагом там ...! –

28

Не совсем то, что вы просите, но в 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 fpInt

+4

Бах, я совсем забыл о ближайших и дальних указателях. Я знал о их существовании, но когда я писал этот вопрос, они полностью сошли с ума. –

+4

Является ли 16-разрядный DOS примером для стандартного компилятора? – sellibitze

+0

@sellibitze: Вы правы - атрибут 'far' не находится ни в одном стандарте C, поэтому фрагмент ответа не является допустимым стандартом C. Таким образом, это, вероятно, не совсем отвечает на вопрос (который, кажется, спрашивает о стандарте C) - но это по-прежнему ценный ответ ИМХО. – sleske

7

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

+0

Гарвардская архитектура часто используется во встроенных процессорах (ПИК) в 2013 году. – chux

+0

Да, архитектура Гарварда проявляется во множестве встроенных микросхем. Но знаете ли вы об одном, который реализует указатели функций, которые отличаются от других указателей на одной платформе? – dmckee

+2

PIC16 (компилятор CCS) использовал тупое 9-10-битное ОЗУ (1-2-битная реверс страницы и 8-битное смещение.) Громоздко даже для 'memcpy()'. Энергонезависимые данные застревают в ПЗУ (я думаю, что 14-16 бит даже адрес указывает на 1 байт) и использовал специальный 'memcpy()/strcpy()'. Функции трудно получить/использовать с помощью указателя на функцию, также имеют 14-16-разрядный адрес в половине 14-битного слова инструкции, поэтому адрес должен быть четным. Конечно, я испортил эту историю, поскольку я использую PIC24 гораздо чаще и позволяю компилятору иметь дело с адресами, зная, что я, кодер, не должен wily-nilly смешивать типы указателей. – chux

13

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

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

+7

Это зависит от вашего определения «логически»; v) – Potatoswatter

+0

Как можно использовать 'malloc()' для выделения, а затем назначить 'int *'? Кроме того, вы имели в виду «Есть не более 2^32 возможных значений для _int * _» или «для int»? – smci

+0

@smci: Я имел в виду 'int *'.Дело в том, что при условии, что для 'int *' не более чем '2^32' юридических значений, тогда неважно, какой размер' int * 'или какие значения являются юридическими, реализация может все еще реализуют преобразование из 'int *' в 4-байтовый 'void *' и обратно. Вы можете выделить и затем присвоить 'int *' используя 'malloc' следующим образом:' int ** ppint = malloc (sizeof (* ppint)); * ppint = 0; '. Но я не вижу, что это касается вопроса или моего ответа. –

7

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

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

Чтобы указать на данные на одной и той же кодовой странице, вы указываете только 16-разрядный адрес; это ближайший указатель.

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

6

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

Оказалось, что для создания gcc-бэндов нужна боль!:)

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