Ответ 1
Он документирует ваше намерение - вы будете хранить небольшие числа, а не символ.
Также он выглядит лучше, если вы используете другие typedefs, такие как uint16_t
или int32_t
.
В чем преимущество использования uint8_t
над unsigned char
в C?
Я знаю, что почти каждая система uint8_t
является просто typedef для unsigned char
,
так зачем использовать его?
Он документирует ваше намерение - вы будете хранить небольшие числа, а не символ.
Также он выглядит лучше, если вы используете другие typedefs, такие как uint16_t
или int32_t
.
Чтобы быть педантичным, некоторые системы могут не иметь 8-битного типа. Согласно Википедии:
Реализация требуется для определения точных значений целочисленных типов для N = 8, 16, 32 или 64 тогда и только тогда, когда она имеет любой тип, соответствующий требованиям. Не требуется определять их для любого другого N, даже если он поддерживает соответствующие типы.
Таким образом, uint8_t
не гарантированно существует, хотя это будет для всех платформ, где 8 бит = 1 байт. Некоторые встроенные платформы могут быть разными, но это становится очень редкими. Некоторые системы могут определять типы char
как 16 бит, и в этом случае, вероятно, не будет 8-битного типа любого типа.
Помимо этого (второстепенного) вопроса, ответ @Mark Ransom является лучшим, на мой взгляд. Используйте тот, который наиболее четко показывает, для чего вы используете данные.
Кроме того, я предполагаю, что вы имели в виду uint8_t
(стандартный typedef из C99, представленный в заголовке stdint.h
), а не uint_8
(не uint_8
частью какого-либо стандарта).
Все дело в том, чтобы написать независимый от реализации код. unsigned char
не гарантированно является 8-разрядным типом. uint8_t
(если доступно).
Как вы сказали, "почти каждая система".
char
, вероятно, является одним из наиболее вероятных изменений, но как только вы начнете использовать uint16_t
и друзей, использование uint8_t
будет лучше, а может даже быть частью стандарта кодирования.
В моем опыте есть два места, где мы хотим использовать 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.
Существует несколько предостережений с использованием упакованных структур. Самая большая проблема заключается в том, что вы должны избегать разыменования адреса члена. В системах с согласованными словами mutibyte это может привести к ошибочному исключению - и coredump.
Некоторые люди также будут беспокоиться о производительности и утверждать, что использование этих упакованных структур замедлит работу вашей системы. Верно, что за кулисами компилятор добавляет код для доступа к неуравновешенным элементам данных. Это можно увидеть, посмотрев код сборки в вашей среде IDE.
Но поскольку упакованные структуры наиболее полезны для связи и хранения данных, тогда данные могут быть извлечены в нефасованное представление при работе с ним в памяти. Обычно нам не нужно работать со всем пакетом данных в памяти.
Вот несколько соответствующих обсуждений:
pragma pack (1) и __attribute__ ((выровненный (1))) работает
Является ли gcc __ атрибут __ ((упакован))/#pragma pack небезопасным?
http://solidsmoke.blogspot.ca/2010/07/woes-of-structure-packing-pragma-pack.html
Там мало. С точки зрения переносимости char
не может быть меньше 8 бит, и ничто не может быть меньше, чем char
, поэтому, если заданная реализация C имеет неподписанный 8-разрядный целочисленный тип, это будет char
. В качестве альтернативы, он может вообще не иметь этого, и в этот момент любые трюки typedef
являются спорными.
Его можно использовать для лучшего документирования вашего кода в том смысле, что он ясно, что вам нужны 8-битные байты, и ничего больше. Но на практике это разумное ожидание практически где-либо уже (есть платформы DSP, на которых это не так, но шансы на то, что ваш код работает там, тонкий, и вы могли бы также ошибочно использовать статическое утверждение в верхней части своей программы на такая платформа).
Это действительно важно, например, когда вы пишете сетевой анализатор. заголовки пакетов определяются спецификацией протокола, а не тем, как работает конкретный компилятор платформы C.
Почти для каждой системы я встречал uint8_t == unsigned char, но это не гарантируется стандартом C. Если вы пытаетесь написать переносимый код, и имеет значение именно то, что размер памяти, используйте uint8_t. В противном случае используйте unsigned char.