Причины использовать (или нет) stdint - PullRequest
44 голосов
/ 23 марта 2012

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

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

  • codealias.info - этот рассказ о переносимости 1011 * стандартного набора.

  • stackoverflow - этот более конкретен для uint8_t .

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

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

Спасибо!

Ответы [ 4 ]

68 голосов
/ 23 марта 2012

Плюсы

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

Использование int и т. Д. Также затрудняет обнаружение промо-акций опасного типа.

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

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

Против

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


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

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

16 голосов
/ 23 марта 2012

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

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

  • чтение / запись на диск / сеть (но тогда вам также нужно использовать функции преобразования в порядковый номер)
  • когда вы хотите, чтобы поведение без знака было с точной обрезкой (но это можно сделать более переносимо с помощью оператора &).
  • когда вы управляете точным макетом структуры, потому что вам нужно убедиться, что нет заполнения (например, для 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 для подсчета. Не думайте использовать что-либо еще, если у вас нет веских причин.

7 голосов
/ 23 марта 2012

cons

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

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

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

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

6 голосов
/ 23 марта 2012

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

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

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

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

...