В современном кроссплатформенном мире C ++ (или C) мы имеем :
Data model | short | int | long | long long | pointers/size_t | Sample operating systems
...
LLP64/IL32P64 16 32 32 64 64 Microsoft Windows (x86-64 and IA-64)
LP64/I32LP64 16 32 64 64 64 Most Unix and Unix-like systems, e.g. Solaris, Linux, BSD, and OS X; z/OS
...
Сегодня это означает, что для любого «общего» (подписанного) целого числа int
будет достаточно и, возможно, все еще может использоваться как целочисленный тип по умолчанию при написании кода приложения C ++. Он также - для текущих практических целей - будет иметь одинаковый размер для разных платформ.
Если для варианта использования требуется как минимум 64 бита, мы можем использовать его сегодня long long
, хотя, возможно, использование одного из типов, определяющих битность, или __int64
типа может иметь больше смысла.
Это остается long
посередине, и мы рассматриваем полный запрет на использование long
кода нашего приложения .
Будет ли это иметь смысл , или есть ли смысл для использования long
в современном C ++ (или C) коде, который должен работать кроссплатформенно? (платформа для настольных компьютеров, мобильных устройств, но не для микроконтроллеров, DSP и т. д.)
Возможно интересные справочные ссылки:
- Какой стандарт C ++ указывает размер типа int, long?
- Почему команда Win64 выбрала модель LLP64?
- 64-битные модели программирования: почему LP64? (несколько лет)
- Является ли
long
гарантированно быть по крайней мере 32 бита? (Это касается обсуждения комментариев ниже. Ответ .)
источник
long
это единственный способ гарантировать 32 бита.int
может быть 16 бит, поэтому для некоторых приложений этого недостаточно. Да,int
иногда это 16 бит на современных компиляторах. Да, люди пишут программы на микроконтроллерах. Я бы сказал, что все больше людей пишут программное обеспечение, которое имеет больше пользователей на микроконтроллерах, чем на ПК, с ростом устройств iPhone и Android, не говоря уже о росте Arduinos и т. Д.int
еще очень много 16 бит. Ненавижу это говорить, но если вы собираетесь писать о «современном кроссплатформенном мире», вы не можете игнорировать весь индийский субконтинент.Ответы:
Единственная причина, по которой я бы использовал
long
сегодня, - это вызов или реализация внешнего интерфейса, который его использует.Как вы говорите в своем посте, short и int сегодня имеют достаточно стабильные характеристики для всех основных настольных / серверных / мобильных платформ, и я не вижу причин для того, чтобы это изменилось в обозримом будущем. Так что я вижу мало причин избегать их вообще.
long
с другой стороны, это беспорядок. На всех 32-битных системах я знаю, что это имеет следующие характеристики.Большой объем кода был написан на основе одной или нескольких из этих характеристик. Однако с переходом на 64-битную версию не удалось сохранить их все. Unix-подобные платформы использовались для LP64, который сохранил характеристики 2 и 3 за счет характеристики 1. Win64 пошел за LLP64, который сохранил характеристику 1 за счет характеристик 2 и 3. В результате вы больше не можете полагаться ни на одну из этих характеристик. и что ИМО оставляет мало причин для использования
long
.Если вы хотите тип, который имеет размер 32 бита, вы должны использовать
int32_t
.Если вы хотите, чтобы тип был такого же размера, как указатель, вы должны использовать
intptr_t
(или лучшеuintptr_t
).Если вам нужен тип, который является самым большим элементом, над которым можно работать в одном регистре / инструкции, то, к сожалению, я не думаю, что стандарт предоставляет его.
size_t
должно быть правильным на большинстве распространенных платформ, но не на x32 .PS
Я не стал бы беспокоиться о «быстрых» или «наименее» типах. «Наименьший» тип имеет значение только в том случае, если вы заботитесь о переносимости, чтобы действительно скрыть архитектуру, где
CHAR_BIT != 8
. Размер «быстрых» типов на практике кажется довольно произвольным. Linux, кажется, делает их по крайней мере того же размера, что и указатель, что глупо на 64-битных платформах с быстрой 32-битной поддержкой, такой как x86-64 и arm64. IIRC iOS делает их как можно меньше. Я не уверен, что делают другие системы.PPS
Одна из причин использования
unsigned long
(но неlong
понятная) заключается в том, что поведение гарантировано по модулю. К сожалению, из-за испорченных правил продвижения C неподписанные типы меньше, чемint
не по модулю поведения.На всех основных платформах сегодня
uint32_t
имеет такой же размер или больше, чем int, и, следовательно, имеет поведение по модулю. Однако были исторически и теоретически могут быть в будущих платформах, гдеint
является 64-битным и, следовательноuint32_t
, не по модулю поведения.Лично я бы сказал, что лучше привыкнуть к принудительному поведению по модулю, используя «1u *» или «0u +» в начале ваших уравнений, поскольку это будет работать для любого размера беззнакового типа.
источник
Как вы упоминаете в своем вопросе, современное программное обеспечение - это взаимодействие между платформами и системами в Интернете. Стандарты C и C ++ дают диапазоны для целочисленных размеров шрифта, а не для конкретных размеров (в отличие от языков, таких как Java и C #).
Чтобы ваше программное обеспечение, скомпилированное на разных платформах, работало с одними и теми же данными одинаково, и чтобы другие программы могли взаимодействовать с вашим программным обеспечением с одинаковыми размерами, вы должны использовать целые числа фиксированного размера.
Введите,
<cstdint>
который обеспечивает именно это и является стандартным заголовком, который должны предоставить все платформы компилятора и стандартной библиотеки. Примечание: этот заголовок был обязателен только для C ++ 11, но многие более ранние реализации библиотеки предоставили его в любом случае.Хотите 64-битное целое число без знака? Используйте
uint64_t
. 32-разрядное целое число со знаком? Используйтеint32_t
. Хотя типы в заголовке являются необязательными, современные платформы должны поддерживать все типы, определенные в этом заголовке.Иногда требуется конкретная битовая ширина, например, в структуре данных, используемой для связи с другими системами. В других случаях это не так. Для менее строгих ситуаций
<cstdint>
предусмотрены типы с минимальной шириной.Существует наименьшее количество вариантов:
int_leastXX_t
будет целочисленный тип с минимальными XX битами. Он будет использовать наименьший тип, который предоставляет XX бит, но тип может быть больше указанного количества бит. На практике они обычно такие же, как типы, описанные выше, которые дают точное количество битов.Существуют также быстрые варианты:
int_fastXX_t
по крайней мере XX бит, но следует использовать тип, который работает быстро на конкретной платформе. Определение «быстрый» в этом контексте не определено. Однако на практике это обычно означает, что тип, меньший, чем размер регистра ЦП, может иметь псевдоним типа размера регистра ЦП. Например, заголовок Visual C ++ 2015 указывает, чтоint_fast16_t
это 32-разрядное целое число, потому что 32-разрядная арифметика в целом быстрее на x86, чем 16-разрядная арифметика.Это все важно, потому что вы должны иметь возможность использовать типы, которые могут содержать результаты вычислений, которые ваша программа выполняет независимо от платформы. Если программа выдает правильные результаты на одной платформе, но неверные результаты на другой из-за различий в целочисленном переполнении, это плохо. Используя стандартные целочисленные типы, вы гарантируете, что результаты на разных платформах будут одинаковыми в отношении размера используемых целых чисел (конечно, между платформами могут быть и другие различия, кроме целочисленной ширины).
Так что да,
long
следует запретить использование современного кода C ++. Так должноint
,short
иlong long
.источник
std
пространство имен, когда#include
d в модуле компиляции C ++, но в документации, которую я связал, об этом не упоминается, и Visual Studio, похоже, не волнует, как я к ним обращаюсь.int
может быть ... чрезмерным? (Я бы подумал, если код должен быть чрезвычайно переносимым на всех непонятных (и не очень непонятных) платформах. Запретить его за «код приложения» может не очень хорошо сработаться с нашими разработчиками.#include <cstdint>
это требуется , чтобы поместить типы вstd::
и ( к сожалению) , необязательно допускается также , чтобы поместить их в глобальном пространстве имен.#include <stdint.h>
это как раз обратное. То же относится и к любой другой паре заголовков C. Смотрите: stackoverflow.com/a/13643019/2757035 Я бы хотел, чтобы Стандарт требовал, чтобы каждый из них влиял только на свое соответствующее требуемое пространство имен - а не, казалось бы, отказывался от некачественных соглашений, установленных некоторыми реализациями, - но, хорошо, мы здесь.Нет, запрещать встроенные целочисленные типы было бы абсурдно. Однако ими не следует злоупотреблять.
Если вам нужно целое число шириной ровно N бит, используйте (или если вам нужна версия). Думать как 32-битное целое и как 64-битное целое просто неправильно. Может случиться так на ваших текущих платформах, но это зависит от поведения, определенного реализацией.
std::intN_t
std::uintN_t
unsigned
int
long long
Использование целочисленных типов фиксированной ширины также полезно для взаимодействия с другими технологиями. Например, если некоторые части вашего приложения написаны на Java, а другие на C ++, вы, вероятно, захотите сопоставить целочисленные типы, чтобы получить согласованные результаты. (Следует помнить, что переполнение в Java имеет четко определенную семантику, в то время как
signed
переполнение в C ++ является неопределенным поведением, поэтому согласованность является высокой целью.) Они также будут иметь неоценимое значение при обмене данными между различными вычислительными хостами.Если вам не нужно ровно N битов, а просто достаточно широкий тип , рассмотрите возможность использования (оптимизировано для пространства) или (оптимизировано для скорости). Опять же, у обеих семей есть коллеги тоже.
std::int_leastN_t
std::int_fastN_t
unsigned
Итак, когда использовать встроенные типы? Ну, так как стандарт не определяет их ширину точно, используйте их, когда вас не интересует фактическая ширина бита, а другие характеристики.
A
char
- это наименьшее целое число, адресуемое аппаратным обеспечением. Язык фактически заставляет вас использовать его для псевдонимов произвольной памяти. Это также единственный жизнеспособный тип для представления (узких) символьных строк.int
Обычно будет самым быстрым типом машина может работать. Он будет достаточно широким, чтобы его можно было загружать и хранить с помощью одной инструкции (без необходимости маскировать или сдвигать биты), и достаточно узким, чтобы его можно было использовать с (наиболее) эффективными аппаратными инструкциями. Следовательно,int
это идеальный выбор для передачи данных и выполнения арифметических операций, когда переполнение не является проблемой. Например, основным типом перечислений по умолчанию являетсяint
. Не меняйте его на 32-битное целое число только потому, что вы можете. Кроме того, если у вас есть значение, которое может быть только -1, 0 и 1,int
Это идеальный выбор, если только вы не собираетесь хранить огромные массивы из них, и в этом случае вы можете использовать более компактный тип данных за счет того, что вам придется платить более высокую цену за доступ к отдельным элементам. Более эффективное кэширование, скорее всего, окупится. Многие функции операционной системы также определены в терминахint
. Было бы глупо конвертировать свои аргументы и результаты туда и обратно. Все, что это может сделать, это ввести ошибки переполнения.long
обычно это самый широкий тип, который может быть обработан с помощью одной машинной инструкции. Это делает его особенноunsigned long
привлекательным для работы с необработанными данными и всеми видами манипуляций с битами. Например, я бы ожидал увидетьunsigned long
в реализации бит-вектор. Если код написан аккуратно, то не имеет значения, насколько он на самом деле широк (потому что код будет адаптироваться автоматически). На платформах, где собственное машинное слово является 32-битным, наличие резервного массива бит-вектора будет массивомunsigned
32-битные целые числа наиболее желательны, потому что было бы глупо использовать 64-битный тип, который должен загружаться с помощью дорогих инструкций, только чтобы в любом случае снова сдвинуть и замаскировать ненужные биты. С другой стороны, если размер собственного слова платформы составляет 64 бита, я хочу массив этого типа, потому что это означает, что такие операции, как «найти первый набор», могут выполняться в два раза быстрее. Таким образом, «проблема» типаlong
данных, который вы описываете, в том, что его размер варьируется от платформы к платформе, на самом деле является функцией, которую можно использовать с пользой. Это становится проблемой только в том случае, если вы думаете о встроенных типах как о типах определенной ширины в битах, чего у них просто нет.char
,int
Иlong
очень полезные типы , как описано выше.short
иlong long
не так полезны, потому что их семантика гораздо менее ясна.источник
long
между Windows и Unix. Возможно, я неправильно понимаю, но ваше описание различий в размерахlong
«функции» вместо «проблемы» имеет смысл для сравнения 32- и 64-разрядных моделей данных, но не для этого конкретного сравнения. В конкретном случае этот вопрос задается, действительно ли это особенность? Или это особенность в других ситуациях (то есть в целом) и безвредная в этом случае?uint32_t
значений будет осуществляться в подписанном ,int
-Width арифметике на платформе , гдеint
находится шире , чемuint32_t
. (С сегодняшним ABI это, скорее всего, будет проблемой дляuint16_t
.)long
обычно будет самым широким шрифтом, который может быть обработан с помощью одной машинной инструкции. ...» - и это совершенно неправильно . Посмотрите на модель данных Windows. ИМХО, весь ваш следующий пример ломается, потому что на x64 Windows long все еще 32 бит.В другом ответе уже подробно рассматриваются типы cstdint и менее известные их варианты.
Я хотел бы добавить к этому:
использовать доменные имена типов
То есть, не объявляйте ваши параметры и переменные
uint32_t
(конечно, нетlong
!), Но имена, такие какchannel_id_type
,room_count_type
и т. Д.о библиотеках
Сторонние библиотеки, которые используют
long
или еще много чего могут раздражать, особенно если они используются в качестве ссылок или указателей на них.Лучше вещь , чтобы сделать обертки.
В общем, моя стратегия состоит в том, чтобы создать набор функций, похожих на приведение, которые будут использоваться. Они перегружены, чтобы принимать только те типы, которые точно соответствуют соответствующим типам, а также любые вариации указателя и т. Д., Которые вам нужны. Они определены специально для os / compiler / settings. Это позволяет удалять предупреждения и в то же время обеспечивать использование только «правильных» преобразований.
В частности, с различными примитивными типами, производящими 32 бита, ваш выбор способа
int32_t
определения может не соответствовать вызову библиотеки (например, int vs long в Windows).Подобная приведению функция документирует конфликт, обеспечивает проверку во время компиляции результата, соответствующего параметру функции, и удаляет любое предупреждение или ошибку, если и только если фактический тип соответствует действительному размеру. То есть он перегружен и определен, если я передаю (в Windows) a
int*
или along*
и выдает ошибку времени компиляции в противном случае.Таким образом, если библиотека обновляется или кто-то изменяет что-то
channel_id_type
, это продолжает проверяться.источник