Кто определяет размер любого типа данных или структуры (в зависимости от 32-битного или 64-битного)?

Кто определяет размер любого типа данных или структуры (в зависимости от 32-битного или 64-битного)? Компилятор или процессор? Например, sizeof(int) составляет 4 байта для 32-битной системы, тогда как 8 бит для 64-разрядной системы.

Я также читал, что sizeof(int) составляет 4 байта при компиляции с использованием как 32-битного, так и 64-битного компилятора.

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

Ответ 1

Это в конечном счете компилятор. Разработчики компилятора могут решить эмулировать любой целочисленный размер, который им подходит, независимо от того, что процессор обрабатывает наиболее эффективно. Тем не менее, стандарт C (и C++) написан таким образом, что разработчик компилятора может выбрать самый быстрый и эффективный способ. Для многих компиляторов разработчики решили сохранить int как 32 бит, хотя процессор изначально обрабатывает 64-битные ints очень эффективно.

Я думаю, что это было сделано частично, чтобы повысить переносимость программ, написанных, когда 32-битные машины были наиболее распространенными и ожидали, что int будет 32 бит и больше не будет. (Также может быть, как указывает пользователь user3386109, что 32-битные данные были предпочтительнее, поскольку они занимают меньше места и, следовательно, могут быть доступны быстрее).

Поэтому, если вы хотите убедиться, что вы получаете 64-битные int, вы используете int64_t вместо int чтобы объявить свою переменную. Если вы знаете, что ваше значение будет соответствовать 32 битам или вам не нужен размер, вы можете использовать int чтобы компилятор мог выбрать наиболее эффективное представление.

Что касается других типов данных, таких как struct, они состоят из базовых типов, таких как int.

Ответ 2

Это не процессор, ни компилятор, ни операционная система. Это все три одновременно.

Компилятор не может просто разобраться. Он должен придерживаться права ABI [1], что обеспечивает операционная система. Если структуры и системные вызовы, предоставляемые операционной системой, имеют типы с определенными размерами и требованиями к выравниванию, компилятор не может свободно создавать свою собственную реальность, если разработчики компилятора не хотят переопределять функции-оболочки для всего, что предоставляет операционная система. Тогда ABI операционной системы не может быть просто полностью составлен, он должен делать то, что можно разумно сделать на CPU. И очень часто ABI одной операционной системы будет очень похож на другие ABI для других операционных систем на одном CPU, потому что проще просто повторить работу, которую они сделали (в компиляторах между прочим).

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

[1] ABI означает Application Binary Interface. Это набор правил взаимодействия программ с операционной системой. Он определяет, как программа хранится на диске для запуска операционной системой, как выполнять системные вызовы, как связываться с библиотеками и т.д. Но для того, чтобы иметь возможность ссылаться на библиотеки, например, ваша программа и библиотека должны соглашаться о том, как делать вызовы функций между вашей программой, библиотекой (и наоборот) и иметь возможность совершать вызовы функций, как программа, так и библиотека должны иметь одинаковую идею о расположении стека, использовании регистров, условных вызовах функций и т.д. А для вызовов функций вам нужно согласовать, что означают параметры, и включает в себя размеры, выравнивание и подпись типов.

Ответ 3

Это строго 100%, полностью компилятор, который определяет значение sizeof (int). Это не комбинация системы и компилятора. Это всего лишь компилятор (и спецификации языка C/C++).

Если вы разрабатываете приложения для iPad или iPhone, вы выполняете компилятор на своем Mac. Mac и iPhone/iPac используют разные процессоры. Ничто из вашего Mac не сообщает компилятору, какой размер следует использовать для int на iPad.

Ответ 4

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

Стандарт C устанавливает минимальные требования для встроенных типов. "char" должен быть не менее 8 бит, "короткий" и "int" должен быть не менее 16 бит, "long" должен быть не менее 32 бит, а "long long" должен быть не менее 64 бит. В нем также говорится, что "char" должен быть эквивалентен наименьшей единице памяти, которую может решить программа, и что порядок размеров стандартных типов должен поддерживаться.

Другие стандарты также могут оказать влияние. Например, версия 2 "единой спецификации Unix" говорит, что int должен быть не менее 32 бит.

Наконец, существующий код оказывает влияние. Портирование уже достаточно тяжело, никто не хочет сделать это сложнее, чем нужно.


При переносе ОС и компилятора на новый процессор кто-то должен определить, что известно как "C ABI". Это определяет, как двоичный код взаимодействует друг с другом.

  • Требования к размеру и выравниванию встроенных типов.
  • Правила упаковки для конструкций (и, следовательно, их размер).
  • Как параметры передаются и возвращаются
  • Как управлять стеком

В общем случае один раз и ABI определен для комбинации семейства процессоров и ОС, он не сильно меняется (иногда размер более неясных типов, таких как "длинные двойные" изменения). Изменение его приносит кучу поломки для относительно небольшого выигрыша.

Аналогично, перенос ОС на платформу со схожими характеристиками с существующей будет обычно выбирать те же размеры, что и на предыдущих платформах, на которые была перенесена ОС.


На практике поставщики ОС/компиляторов обычно располагаются в одной из нескольких комбинаций размеров для основных целых типов.

  • "LP32": char - 8 бит. short и int - 16 бит, long и указатель - 32 бит. Обычно используется на 8-битных и 16-разрядных платформах.
  • "ILP32": char - 8 бит, короткий - 16 бит. int, long и указатель - все 32 бита. Если долгое время существует, это 64 бит. Обычно используется на 32-битных платформах.
  • "LLP64": char - 8 бит. короткий - 16 бит. int и long - 32 бита. long long и указатель - 64 бит. Используется в 64-битных окнах.
  • "LP64": char - 8 бит. короткий - 16 бит. int - 32 бита. длинный, длинный и указательный - 64 бит. Используется в большинстве 64-разрядных Unix-подобных систем.
  • "ILP64": char - 8 бит, короткий - 16 бит, int, long и указатель и long long - все 64 бит. По-видимому, он используется на некоторых ранних 64-битных операционных системах, но редко встречается в наши дни.

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

Ответ 5

Компилятор решает, насколько велики основные типы и каков макет структур. Если библиотека объявляет какие-либо типы, она решит, как они определены и, следовательно, какой размер они являются.

Однако часто бывает, что совместимость с существующим стандартом и необходимость подключения к существующим библиотекам, созданным другими компиляторами, заставляют данную реализацию сделать определенные выборы. Например, в стандарте языка говорится, что wchar_t должен быть шире 16 бит, а в Linux - 32 бита, но он всегда был 16 бит в Windows, поэтому компиляторы для Windows все хотят быть совместимыми с Windows API вместо стандартного языка. Многие устаревшие коды для Linux и Windows предполагают, что long составляет ровно 32 бита, в то время как другой код предполагает, что он достаточно широк, чтобы удерживать отметку времени в секундах или IPv4-адрес или смещение файла или биты указателя, и (после того, как один компилятор определил int как 64 бита шириной и long 32 бита в ширину), языковой стандарт сделал новое правило, что int не может быть шире, чем long.

В результате основные компиляторы из этого столетия решили определить int как 32 бита в ширину, но исторически некоторые из них определили его как 16 бит, 18 бит, 32 бита, 64 бита и другие размеры. Некоторые компиляторы позволяют вам выбирать, будет ли long будет ровно 32 бита в ширину, как предполагает какой-то старый код или такой же широкий, как указатель, как предполагает другой старый код.

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

Но что вы на самом деле используете?

Тип int редко используется в наши дни. Обычно, как правило, какой-то другой тип, который вы можете использовать, делает более надежную гарантию того, что вы получите. (У него есть одно преимущество: типы, которые arent настолько широки, что int может автоматически расширяться до int, что может вызвать несколько действительно странных ошибок при смешивании типов с подписью и без знака, а int - наименьший тип, который не должен быть короче, чем int.)

Если вы используете конкретный API, вы, как правило, захотите использовать тот же тип, что и он. Существует множество типов в стандартной библиотеке для конкретных целей, таких как clock_t для тиков часов и time_t для времени в секундах.

Если вам нужен самый быстрый тип шириной не менее 16 бит, это int_fast16_t, и есть другие подобные типы. (Если не указано иное, все эти типы определены в <stdint.h>.) Если вы хотите, чтобы наименьший тип имел ширину не менее 32 бит, чтобы упаковать большинство данных в ваши массивы, это int_least32_t. Если вам нужен максимально возможный тип, то intmax_t. Если вы знаете, что хотите ровно 32 бита, а ваш компилятор имеет такой тип, его int32_t Если вы хотите, чтобы 32-разрядная int32_t была 32-разрядной и 64-разрядной, и всегда правильный размер для хранения указателя, thats intptr_t. Если вам нужен хороший тип для индексирования массива и математики указателя, то ptrdiff_t из <stddef.h>. (Это в другом заголовке, потому что это от C89, а не C99.)

Используйте тип, который вы действительно имеете в виду!

Ответ 6

Когда вы говорите о компиляторе, вы получаете чистое изображение о build|host|target, то есть машине, на которой вы строите (сборку), машине, которую вы строите для (хост), и машине, которую GCC будет производить код для (target), потому что для "кросс-компиляции" очень отличается от "native compiling".

Что касается вопроса "кто определяет размер и тип данных", это зависит от целевой системы, которую вы сказали компилятору для создания двоичного кода. Если целью является 64 бита, компилятор переведет sizeof (long) в 8, и если цель будет 32-битной машиной, компилятор переведет sizeof (long) в 4. Все они были предопределены файлом заголовка, который вы использовали для сборки вашей программы. Если вы читаете "$ MAKETOP/usr/include/stdint.h", для определения размера вашего типа данных существуют typedefs.

Чтобы избежать ошибки, созданной разницей в размерах, стиль кодирования Google-Integer_Types рекомендует использовать такие типы, как int16_t, uint32_t, int64_t и т.д. Они были определены в <stdint.h>.

Выше представлены только те "Обычные старые данные", как int. Если вы говорите о структуре, есть еще одна история, потому что размер структуры зависит от выравнивания упаковки, выравнивания границ для каждого поля в структуре, что окажет влияние на размер структуры.

Ответ 7

Это компилятор, а точнее его компонент генератора кода.

Конечно, компилятор обладает поддержкой архитектуры и делает выбор, соответствующий этому.

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