uint8_t против неподписанного символа - PullRequest
209 голосов
/ 13 ноября 2009

В чем преимущество использования uint8_t над unsigned char в C?

Я знаю, что почти в каждой системе uint8_t это просто определение типа для unsigned char, так зачем его использовать?

Ответы [ 8 ]

204 голосов
/ 13 ноября 2009

Он документирует ваше намерение - вы будете хранить маленькие цифры, а не символ.

Также это выглядит лучше, если вы используете другие определения типа, такие как uint16_t или int32_t.

63 голосов
/ 13 ноября 2009

Просто чтобы быть педантичным, некоторые системы могут не иметь 8-битного типа. Согласно Википедии :

Требуется реализация для определения целочисленных типов точной ширины для N = 8, 16, 32 или 64, если и только если она имеет какой-либо тип, отвечающий требованиям. Не требуется определять их для любого другого N, даже если он поддерживает соответствующие типы.

Так что uint8_t не гарантированно существует, хотя это будет для всех платформ, где 8 бит = 1 байт. Некоторые встроенные платформы могут отличаться, но это происходит очень редко. Некоторые системы могут определять типы char как 16-битные, в этом случае, вероятно, не будет 8-битного типа.

Кроме этой (незначительной) проблемы, @ Ответ Марка Рэнсома - лучший на мой взгляд. Используйте тот, который наиболее четко показывает, для чего вы используете данные.

Кроме того, я предполагаю, что вы имели в виду uint8_t (стандартный typedef от C99, предоставленный в заголовке stdint.h), а не uint_8 (не является частью какого-либо стандарта).

40 голосов
/ 13 ноября 2009

Весь смысл в том, чтобы написать независимый от реализации код. unsigned char не гарантированно будет 8-битным типом. uint8_t есть (если доступно).

7 голосов
/ 03 марта 2014

По моему опыту, есть два места, где мы хотим использовать uint8_t для обозначения 8 бит (и uint16_t и т. Д.) И где мы можем иметь поля размером менее 8 бит. В обоих случаях пространство имеет значение, и нам часто приходится смотреть на необработанный дамп данных при отладке и иметь возможность быстро определить, что оно представляет.

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

#pragma pack(1)
typedef struct {
  uint8_t    flag1:1;
  uint8_t    flag2:1;
  padding1   reserved:6;  /* not necessary but makes this struct more readable */
  uint32_t   sequence_no;
  uint8_t    data[8];
  uint32_t   crc32;
} s_mypacket __attribute__((packed));
#pragma pack()

Какой метод вы используете, зависит от вашего компилятора. Вам также может потребоваться поддержка нескольких разных компиляторов с одинаковыми заголовочными файлами. Это происходит во встроенных системах, где устройства и серверы могут быть совершенно разными - например, у вас может быть устройство ARM, которое взаимодействует с сервером Linux x86.

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

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

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

Вот некоторые соответствующие обсуждения:

Pragma Pack (1), ни __attribute__ ((выровненный (1))) работает

Является ли __attribute gcc __ ((упакованный)) / #pragma pack небезопасным?

http://solidsmoke.blogspot.ca/2010/07/woes-of-structure-packing-pragma-pack.html

7 голосов
/ 13 ноября 2009

Как вы сказали, " почти каждая система".

char, вероятно, является одним из менее вероятных изменений, но как только вы начнете использовать uint16_t и друзей, использование uint8_t лучше сочетается и может даже стать частью стандарта кодирования.

6 голосов
/ 13 ноября 2009

Там мало. С точки зрения переносимости, char не может быть меньше 8 бит, и ничто не может быть меньше, чем char, поэтому, если данная реализация C имеет 8-разрядный целочисленный тип без знака, она будет char. Кроме того, он может вообще не иметь ни одного, и в этот момент любые typedef трюки являются спорными.

Это может быть использовано для лучшего документирования вашего кода в том смысле, что ясно, что вам требуются 8-битные байты и ничего больше. Но на практике это разумное ожидание практически где-то уже (есть платформы DSP, на которых это не так, но шансы на выполнение вашего кода там невелики, и вы также можете ошибиться, используя статическое утверждение в верхней части вашей программы на такая платформа).

3 голосов
/ 01 марта 2010

Это действительно важно, например, когда вы пишете сетевой анализатор. заголовки пакетов определяются спецификацией протокола, а не тем, как работает компилятор C конкретной платформы.

2 голосов
/ 13 ноября 2009

Почти во всех системах, с которыми я встречался, uint8_t == unsigned char, но это не гарантируется стандартом C. Если вы пытаетесь написать переносимый код, и точно имеет значение, какой объем памяти, используйте uint8_t. В противном случае используйте unsigned char.

...