Причины использования (или нет) stdint

Я уже знаю, что stdint используется, когда вам нужны конкретные размеры переменных для переносимости между платформами. У меня пока нет такой проблемы, но каковы ее плюсы и минусы, помимо уже показанного выше факта?

Ища это на stackoverflow и других сайтах, я нашел 2 ссылки, относящиеся к теме:

Эти две ссылки особенно хороши, если вы хотите узнать больше о главной причине этого заголовка - переносимости. Но для меня больше всего мне нравится то, что я думаю, что uint8_t чище, чем unsigned char (например, для хранения значения канала RBG), int32_t выглядит более значимым, чем просто int и т.д.

Итак, мой вопрос, какие именно плюсы и минусы использования stdint помимо переносимости? Должен ли я использовать его только в некоторых специфических частях моего кода или везде? если везде, как я могу использовать такие функции, как atoi(), strtok() и т.д. с ним?

Спасибо!

Ответ 1

Pros

Использование четко определенных типов делает код намного проще и безопаснее для порта, так как вы не получите никаких сюрпризов, когда, например, один компьютер интерпретирует int как 16-битный, а другой как 32-разрядный. С stdint.h, что вы набираете, вы получаете.

Использование int и т.д. также затрудняет обнаружение опасного типа.

Другим преимуществом является то, что, используя int8_t вместо char, вы знаете, что вы всегда получаете подписанную 8-битную переменную. char может быть подписано или без знака, это поведение, определяемое реализацией, и варьируется между компиляторами. Поэтому по умолчанию char просто опасно использовать в коде, который должен быть переносимым.

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

Против

Отсутствует.


Ссылка: MISRA-C: правило 2004 6.3. "typedefs, которые указывают размер и подпись, должны использоваться вместо базовых типов".

РЕДАКТИРОВАТЬ: Удаленный неправильный пример.

Ответ 2

Единственная причина использовать uint8_t, а не unsigned char (помимо эстетического предпочтения) - это если вы хотите записать, что ваша программа требует char быть ровно 8 бит. uint8_t существует тогда и только тогда, когда CHAR_BIT==8, в соответствии с требованиями стандарта C.

Остальные типы intX_t и uintX_t полезны в следующих ситуациях:

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

С другой стороны, типы uint_least8_t и т.д. полезны везде, где вы хотите избежать использования слишком больших или медленных типов, но должны быть уверены, что вы можете хранить значения определенной величины. Например, в то время как long long не менее 64 бит, это может быть 128-бит на некоторых машинах, и использовать его, когда то, что вам нужно, это просто тип, который может хранить 64-разрядные номера, будет очень расточительным на таких машинах. int_least64_t решает проблему.

Я бы не использовал типы [u]int_fastX_t полностью, так как они иногда менялись на данной машине (нарушая ABI), и поскольку определения обычно неправильны. Например, на x86_64 64-разрядный целочисленный тип считается "быстрым" для 16-, 32- и 64-битных значений, но в то время как сложение, вычитание и умножение являются точно такой же скоростью, независимо от того, используете ли вы 32- бит или 64-разрядные значения, деление почти наверняка происходит медленнее с более крупными, чем необходимо, типами, и даже если они имеют одинаковую скорость, вы используете в два раза больше памяти без каких-либо преимуществ.

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

Ответ 3

против

Основная причина, по которой на языке C не указывается размер int или long и т.д. для эффективности вычислений. Каждая архитектура имеет естественный, наиболее эффективный размер, а дизайнеры специально расширили возможности и предложили разработчику компилятора использовать естественные собственные данные размера данных для скорости и эффективности кода.

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

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

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

Ответ 4

Я использую типы stdint только по одной причине, когда данные, хранящиеся в памяти, должны поступать на диск/сеть/дескриптор в двоичной форме. Вам нужно только бороться с проблемой little-endian/big-endian, но относительно легко преодолеть.

Очевидная причина, по которой не использовать stdint, - это когда размер кода не зависит от размера, в математическом выражении все, что работает над целыми рациональными целями. Это создаст уродливые дубликаты кода, если вы предоставили uint*_t версию, скажем, qsort() для каждого расширения *.

Я использую свои собственные типы в этом случае, полученные из size_t, когда я ленив или самое большое поддерживаемое целое число без знака на платформе, когда я этого не делаю.

Изменить, потому что я столкнулся с этой проблемой раньше:
Я думаю, что стоит отметить, что в Solaris 2.5.1 нарушены не менее uint8_t, uint32_t и uint64_t. Поэтому для максимальной переносимости я все же предлагаю избегать stdint.h (по крайней мере в течение следующих нескольких лет).