sizeof Pointer отличается для типа данных на той же архитектуре

Я просматривал некоторые сообщения и заметил, что указатели могут быть разных размеров в зависимости от размера в зависимости от архитектуры, в которой компилируется и работает код. Для меня это достаточно разумно (т. Е. 4-байтные указатели на 32-битных архитектурах, 8-байтовые на 64-битных, имеют полный смысл).

Меня удивляет то, что размер указателя может отличаться в зависимости от типа данных, на который он указывает. Я бы предположил, что в 32-битной архитектуре все указатели будут иметь размер 4 байта, но оказывается, что указатели на функции могут быть другого размера (т. Е. Больше, чем я ожидал). Почему это, на языке программирования C? Я нашел статью, которая объясняет это для C ++ и как программа может справиться с виртуальными функциями , но это, похоже, не применяется в чистом C. Также кажется, что использование «дальних» и «близких» указателей больше не нужны, поэтому я не вижу тех, кто входит в уравнение.

Итак, в C, какое обоснование, стандарт или документация описывает, почему не все указатели имеют одинаковый размер в одной и той же архитектуре?

Спасибо!

Стандарт C устанавливает закон о том, что требуется:

  • Все указатели данных могут быть преобразованы в void* и обратно без потери информации.
  • Все struct указатели имеют одинаковое представление + выравнивание и могут быть преобразованы друг в друга.
  • Все указатели union имеют одинаковое представление + выравнивание и, таким образом, могут быть преобразованы друг в друга.
  • Все указатели символов и указатели void имеют одинаковое представление + выравнивание.
  • Все указатели на квалифицированные и неквалифицированные совместимые типы должны иметь одинаковое представление + выравнивание. (Например, неподписанные / подписанные версии того же типа совместимы)

  • Все указатели функций имеют одинаковое представление + выравнивание и могут быть преобразованы в любой другой тип указателя функции и обратно.

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

На архитектурах, где указатели – это, естественно, указатели слов вместо указателей символов, вы получаете указатели данных разных размеров.
На архитектурах с разными размерами кода / пространства данных (многие микропроцессоры) или где требуется дополнительная информация для правильных вызовов функций (например, itanium, хотя они часто скрывают это за указателем данных), вы получаете указатели кода разного размера от указатели данных.

Итак, в C, какое обоснование, стандарт или документация описывает, почему не все указатели имеют одинаковый размер в одной и той же архитектуре?

C11: 6.2.5 p (28):

Указатель на void должен иметь те же требования к представлению и выравниванию, что и указатель на тип символа. Точно так же указатели на квалифицированные или неквалифицированные версии совместимых типов должны иметь одинаковые требования к представлению и выравниванию. Все указатели на типы структуры должны иметь одинаковые требования к представлению и согласованию друг с другом. Все указатели на типы union должны иметь одинаковые требования к представлению и согласованию друг с другом. Указатели на другие типы не должны иметь одинаковые требования к представлению или выравниванию .

6.3.2.3 Указатели p (8):

Указатель на функцию одного типа может быть преобразован в указатель на функцию другого типа и обратно; результат сравнивается с исходным указателем. Если преобразованный указатель используется для вызова функции, тип которой несовместим с указанным типом, поведение не определено .

Это поясняет, что указатели на данные и указатели на функции не имеют одинакового размера.

Еще один момент:

Q: Итак, можно ли сказать, что, хотя я не должен явно использовать ключевые слова far / near при определении указателя, это обрабатывается автоматически «под капотом» компилятором?

A: http://www.unix.com/programming/45002-far-pointer.html

Это исторический анахронизм от сегментированных архитектур, таких как 8086.

Вернувшись в прошлое 8080, это был 8-разрядный процессор с 16-разрядной адресной шиной, следовательно, 16-разрядные указатели.

В дополнение к 8086, чтобы поддержать некоторый уровень обратной совместимости, он принял сегментированную архитектуру, которая позволяет использовать 16-разрядные, 20-битные или 32-битные указатели в зависимости от дня недели. Где указатель представлял собой комбинацию 16-разрядного сегмента и 16 бит около смещения. Это приводит к появлению крошечных, малых, средних, больших и огромных моделей памяти с близкими, большими и большими указателями.

Другие архитектуры, такие как 68000, не приняли эту схему и имели так называемую модель с плоской памятью.

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

Я собрал это на трех разных платформах; указатель char * был идентичен указателю на функцию в каждом случае:

КОД:

 #include  int main (int argc, char *argv[]) { char * cptr = NULL; void (*fnptr)() = NULL; printf ("sizeof cptr=%ld, sizeof fnptr=%ld\n", sizeof (cptr), sizeof (fnptr)); return 0; } 

РЕЗУЛЬТАТЫ:

  char ptr fn ptr -------- ------ Win8/MSVS 2013 4 4 Debian7/i686/GCC 4 4 Centos/amd64/GCC 8 8 

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

На некоторых платформах одно адресное пространство будет содержать все остальные, но доступ к вещам в этом адресном пространстве будет медленнее (иногда 2x или более), чем доступ к вещам, которые, как известно, находятся в определенной его части. На других платформах не будет никакого «главного» адресного пространства, поэтому для доступа к вещам в разных пространствах потребуются разные типы указателей.

Я не согласен с утверждением, что существование нескольких адресных пространств следует рассматривать как реликвию. На ряде процессоров ARM можно было бы иметь до 1K-4K (в зависимости от точной чип-карты) от глобальных комбинаций, к которым можно было бы получить доступ в два раза быстрее – и с меньшим количеством кода, чем «нормальный», глобальные переменные. Я не знаю никаких компиляторов ARM, которые будут использовать это, но нет никакой причины, чтобы компилятор для ARM не мог этого сделать.