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