uint8_t против беззнакового символа

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

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


person Lyndon White    schedule 12.11.2009    source источник


Ответы (8)


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

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

person Mark Ransom    schedule 12.11.2009
comment
Явное использование unsigned char или signed char также документирует намерение, поскольку неукрашенный char - это то, что показывает, что вы работаете с символами. - person caf; 13.11.2009
comment
@caf: Если вам посчастливилось выйти за рамки простого «неподписанного» для начала, что я все еще вижу, как люди делают, чтобы позволить платформе выбирать, является ли ее int или char по умолчанию. Но, я думаю, в наши дни «неподписанный» (один или украшенный) адекватно указывает на намерение, иначе это объясняет простой процесс устранения :) - person Tim Post♦; 29.11.2009
comment
Я думал, что unsigned без украшений - это unsigned int по определению? - person Mark Ransom; 29.11.2009
comment
@endolith, использование uint8_t для строки не обязательно неправильно, но определенно странно. - person Mark Ransom; 14.11.2011
comment
Таким образом, uint8_t может содержать целочисленное значение от 0 до 255 или 8 двоичных разрядов. Точно так же, как тогда, когда синтаксис был беззнаковым символом, но с гораздо лучшей грамматикой. - person Hellonearthis; 27.12.2012
comment
@NickSoft, вопрос был не в этих других типах, поэтому я не вдавался в подробности. И, к сожалению, unsigned char и uint8_t не являются отдельными типами, см., Например, ideone.com/GMV0uD - person Mark Ransom; 04.04.2013
comment
Хм. Прошу прощения за неверную информацию ... Я знал, что enum и int являются типами distrinct, и предположил, что то же самое и для других типов, которые могут быть автоматически преобразованы в int. А может это зависит от компилятора ... - person NickSoft; 04.04.2013
comment
@endolith, я думаю, что могу привести аргументы в пользу uint8_t с текстом UTF8. Действительно, char, похоже, подразумевает символ, тогда как в контексте строки UTF8 это может быть всего лишь один байт многобайтового символа. Использование uint8_t может прояснить, что не следует ожидать символа в каждой позиции - другими словами, каждый элемент строки / массива является произвольным целым числом, о котором не следует делать никаких семантических предположений. Конечно, все программисты на C знают это, но это может подтолкнуть начинающих задавать правильные вопросы. - person tne; 16.01.2014
comment
Я должен сказать, что unsigned char на самом деле не используется в первую очередь для хранения символов, поэтому проблема намерений является спорной. - person user541686; 22.07.2014
comment
Что ж, это исторически. Я думаю, мы можем предположить, что он был использован для хранения символов в первую очередь (первоначальное намерение; то, что char является сокращением символа, довольно однозначно), но действительно не на практике < / i>, потому что это был исторически единственный стандартный 8-битный тип данных до появления C99 inttypes.h. Теперь, когда у нас есть inttypes.h, я чувствую, что на самом деле все дело в намерении при сравнении исходных типов данных и новых (u)int_(least/fast)N_t типов данных, а также в намерении и гарантии того, что код либо компилируется с точной шириной, либо не компилируется вообще, когда дело доходит до (u)intN_t. - person tne; 27.11.2017

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

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

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

Помимо этой (незначительной) проблемы, лучше всего подходит ответ @Mark Ransom мое мнение. Используйте тот, который наиболее четко показывает, для чего вы используете данные.

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

person Chris Lutz    schedule 12.11.2009
comment
DSP с CHAR_BIT > 8 становятся менее редкими, не более. - person caf; 13.11.2009
comment
@caf, из чистого любопытства - можешь дать ссылку на описание некоторых? Я знаю, что они существуют, потому что кто-то упомянул один (и связал его с документами разработчика) в управляемом comp.lang.c ++. Обсуждении о том, являются ли гарантии типа C / C ++ слишком слабыми, но я больше не могу найти этот поток, и он всегда удобен чтобы ссылаться на это в любых подобных обсуждениях :) - person Pavel Minaev; 13.11.2009
comment
Некоторые системы могут определять типы char как 16-битные, и в этом случае, вероятно, не будет никакого 8-битного типа. - и, несмотря на некоторые неправильные возражения с моей стороны, Павел продемонстрировал в своем ответе, что если char составляет 16 бит, то даже если компилятор предоставляет 8-битный тип, он не должен называть его uint8_t (или typedef это к тому). Это связано с тем, что у 8-битного типа в представлении хранилища будут неиспользуемые биты, которых uint8_t не должно быть. - person Steve Jessop; 13.11.2009
comment
В архитектуре SHARC есть 32-битные слова. Подробнее см. en.wikipedia.org/wiki/. - person BCran; 13.11.2009
comment
DSP TI C5000 (которые были в OMAP1 и OMAP2) являются 16-битными. Я думаю, что для OMAP3 они выбрали серию C6000 с 8-битным символом. - person Steve Jessop; 13.11.2009
comment
О да, это действительно был SHARC. Спасибо. Мне кажется, идеальная платформа для B (между BCPL и C) :) - person Pavel Minaev; 13.11.2009
comment
Копаемся в N3242 - Рабочий проект, Стандарт языка программирования C ++, раздел 18.4.1 ‹cstdint› синопсис говорит: typedef unsigned integer type uint8_t; // optional Таким образом, по сути, стандартная соответствующая библиотека C ++ не нужна для определения uint8_t вообще (см. Комментарий // необязательно) - person nightlytrails; 23.02.2013
comment
В случаях, когда наименьший тип данных превышает 8 бит (например, серия Ti C2000 они 16-битные), я считаю, что можно использовать uint_least8_t для правильного указания намерения и того факта, что тип не может на самом деле быть 8-битными. - person Toby; 29.05.2015

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

person AnT    schedule 12.11.2009
comment
... если он существует в системе, но это будет очень редко. +1 - person Chris Lutz; 13.11.2009
comment
хорошо, если у вас действительно были проблемы с вашим кодом, который не компилируется в системе, потому что uint8_t не существует, вы можете использовать find и sed для автоматического изменения всех вхождений uint8_t на unsigned char или что-то более полезное для вас. - person bazz; 25.07.2014
comment
@bazz - нет, если вы предполагаете, что это 8-битный тип, который вы не можете - например, для распаковки данных, упакованных побайтно удаленной системой. Неявное предположение состоит в том, что причина отсутствия uint8_t кроется в процессоре, у которого char больше 8 бит. - person Chris Stratton; 12.04.2015
comment
добавить утверждение assert (sizeof (unsigned char) == 8); - person bazz; 13.04.2015
comment
@bazz неверное утверждение, боюсь. sizeof(unsigned char) вернет 1 для 1 байта. но если системный char и int имеют одинаковый размер, например, 16 бит, тогда sizeof(int) также вернет 1 - person Toby; 29.05.2015
comment
ОК .. у кого-нибудь есть решение? - person bazz; 04.06.2015
comment
@bazz #if CHAR_BIT == 8 или #ifdef UINT8_MAX - person chux - Reinstate Monica; 28.02.2018
comment
@jwd: Это неправда. uint8_t гарантированно относится к 8-битному типу. Не гарантируется, что этот тип доступен. Но если он есть, то шириной ровно 8 бит. Верно, что char не гарантированно будет иметь 8-битную ширину, но uint8_t не имеет ничего общего с char. - person AnT; 18.03.2020
comment
О, ты прав; Я кое-что узнал (: Я немного удалю свой комментарий, чтобы случайно никого не ввести в заблуждение. - person jwd; 21.03.2020
comment
гораздо лучше иметь ошибку компилятора, явно показывающую, что беззнаковое 8-битное целое число, которое вы ожидали не существует, чем ваш код задохнется и умрет позже ... ( если, конечно, ваш код не полагается на то, что указанные символы являются 8-битными, и в этом случае конечно не стесняйтесь называть их символами!) - person JamesTheAwesomeDude; 12.06.2020

Как вы сказали, «почти в каждой системе».

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

person Justin Love    schedule 12.11.2009

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

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

#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, которое взаимодействует с сервером x86 Linux.

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

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

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

Вот несколько важных дискуссий:

работает пакет прагмы (1) и __attribute__ ((выровненный (1)))

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

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

person Tereus Scott    schedule 03.03.2014

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

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

person Pavel Minaev    schedule 12.11.2009
comment
Для записи, вы можете создать 8-битный тип на любой платформе: typedef struct { unsigned i :8; } uint8_t;, но вам придется использовать его как uint8_t x; x.i = ..., так что это будет немного громоздко. - person Chris Lutz; 13.11.2009
comment
Я думаю, что символы могут достигать 4 бит, ниже этого, и в стандарте все немного разваливается (хотя есть вероятность, что я ошибаюсь). - person Skizz; 13.11.2009
comment
@Skizz - Нет, стандарт требует, чтобы unsigned char мог хранить значения от 0 до 255. Если вы можете сделать это в 4 битах, снимаю шляпу перед вами. - person Chris Lutz; 13.11.2009
comment
это было бы немного более громоздко - громоздко в том смысле, что вам пришлось бы идти (плавать, садиться на самолет и т. д.) до места, где был составитель компилятора, хлопать его по затылку и заставьте их добавить uint8_t к реализации. Интересно, компиляторы для DSP с 16-битными символами обычно реализуют uint8_t или нет? - person Steve Jessop; 13.11.2009
comment
@ Стив, нет, они этого не делают, потому что у них нет никакого способа сделать это. Уловка с битовым полем действительно работает, но битовые поля очень ограничены (у вас не может быть их массивов, у вас не может быть указателей на них и т. Д.). В C99 нет требования, чтобы стандарт вообще имел uint8_t - он должен иметь его тогда и только тогда, когда он имеет соответствующий тип. Однако требуется предоставить uint8_least_t, что составляет не менее 8 бит (но может быть больше). - person Pavel Minaev; 13.11.2009
comment
Кстати, если подумать, это, пожалуй, самый простой способ сказать, что мне действительно нужны 8 бит - #include <stdint.h>, и использовать uint8_t. Если платформа есть, она вам ее отдаст. Если на платформе его нет, ваша программа не будет компилироваться, и причина будет ясна и очевидна. - person Pavel Minaev; 13.11.2009
comment
Мне нравится логика, что если uint8_t существует вообще, то в любом случае будет unsigned char. - person caf; 13.11.2009
comment
у них действительно нет никакого способа сделать это - ну, это зависит от того, как кодируется компилятор. Вы знаете, что они могут сгенерировать код для выполнения 8-битной беззнаковой арифметики из-за битовых полей (вероятно, нормальная арифметика плюс некоторая маскировка). Конечно, у вас будет sizeof(uint8_t) == sizeof(char), хотя UCHAR_MAX != 255, но это нормально, поэтому типы не должны использовать все свои биты памяти. Под пощечиной я, конечно, подразумеваю страстный, но вежливый запрос. Они имеют право отказаться от этого, но насколько они уверены, что вы не прибегнете к насилию? ;-) - person Steve Jessop; 13.11.2009
comment
Что касается простоты - это, безусловно, наименьшее количество предварительных усилий по кодированию, но, как вы говорите, для настоящей переносимости вам просто нужно использовать uint8_least_t и применить переполнение по модулю 256 для себя. Я предполагаю, что вы можете написать это так, чтобы в любом неопределенно оптимизирующем компиляторе, где uint8_least_t - 8 бит, все лишние операции опускались. - person Steve Jessop; 13.11.2009
comment
Конечно, у вас будет sizeof (uint8_t) == sizeof (char), даже если UCHAR_MAX! = 255, но это нормально, поэтому типы не должны использовать все свои биты хранения. - это не нормально, потому что unsigned char специально требуется для полного использования всех битов хранения как в ISO C, так и в C ++. См. 6.2.6.1/3 (и соответствующую сноску) для C99 и 3.9.1 / 1 для C ++ 03. - person Pavel Minaev; 13.11.2009
comment
Все хорошо. unsigned char (который в этом примере 16-битный) использует все биты, но AFAIK uint8_t не обязательно. Следовательно, uint8_t может быть меньше unsigned char по диапазону, хотя, очевидно, не по размеру хранилища. Поэтому я не понимаю, почему разработчику компилятора должно быть трудно поддерживать uint8_t. Это может быть чудовищно неэффективно, но это отдельная тема. - person Steve Jessop; 13.11.2009
comment
По-прежнему нет сигары, извините: для целочисленных типов без знака, отличных от unsigned char, биты представления объекта должны быть разделены на две группы: биты значений и биты заполнения ... Если имеется N битов значений, каждый бит должен представлять различную мощность of 2 между 1 и 2 ^ (N-1), так что объекты этого типа должны быть способны представлять значения от 0 до 2 ^ (N-1) с использованием чистого двоичного представления ... Имя typedef intN_t обозначает знаковый целочисленный тип шириной N, без битов заполнения и представление с дополнением до двух. - person Pavel Minaev; 13.11.2009
comment
Хорошо, вы выиграли :-). В 7.18.1.1 явно не говорится, что неподписанные версии не имеют битов заполнения. Но это подразумевается требованием о том, что если вы предоставляете uint8_t, вы должны предоставить int8_t и леммой: если uint8_t имеет биты заполнения, тогда int8_t имеет биты заполнения, поскольку они одинаковой ширины и одинакового размера хранилища. - person Steve Jessop; 13.11.2009
comment
Мораль истории: целочисленные типы - это глупо, хотя и быстро. Если вам нужна арифметика по модулю любой конкретной степени двойки, либо напишите ее самостоятельно, либо используйте POSIX-совместимую реализацию, где uint8_t является обязательным ;-) - person Steve Jessop; 13.11.2009
comment
Если вам просто нужна арифметика по модулю, беззнаковое битовое поле подойдет (если это неудобно). Это когда вам нужен, скажем, массив октетов без заполнения, это когда вы SOL. Мораль этой истории - не писать код для DSP, а придерживаться правильной, честной архитектуры 8-битных символов :) - person Pavel Minaev; 13.11.2009
comment
К сожалению, хотя Стандарт требует, чтобы если uint8_t существует, тогда unsigned char также должен быть 8-битным, это не запрещает реализации делать uint8_t 8-битным расширенным целочисленным типом. Было бы действительно полезно иметь 8-битный беззнаковый тип, который не получает специальной обработки псевдонимов, данной для unsigned char, и ничто не запрещает реализации сделать uint8_t таким типом [IMHO, правильный способ определения такого типа было бы дать ему специальное имя, которое могло бы иметь псевдоним uint8_t в реализациях, поддерживающих последнее ... - person supercat; 15.08.2016
comment
... но не иметь 8-битного типа без псевдонима]. - person supercat; 15.08.2016

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

person VP.    schedule 01.03.2010
comment
Когда я спросил об этом, я определил простой протокол для связи по последовательному каналу. - person Lyndon White; 04.06.2011

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

person atlpeg    schedule 12.11.2009
comment
uint8_t всегда соответствует диапазону и размеру unsigned char и заполнению (нет), когда unsigned char 8-битный. Когда unsigned char не является 8-битным, uint8_t не существует. - person chux - Reinstate Monica; 04.12.2016
comment
@chux, у вас есть ссылка на точное место в стандарте, где это написано? Если unsigned char 8-битный, uint8_t гарантированно будет typedef, а не typedef расширенного целочисленного типа без знака? - person hsivonen; 28.02.2018
comment
@hsivonen точное место в стандарте, где это написано? - ›Нет - пока посмотрите 7.20.1.1. Это легко выводится, поскольку unsigned char/signed char/char - это наименьший тип - не менее 8 бит. unsigned char не имеет отступов. Чтобы uint8_t был, он должен быть 8-битным, без заполнения, существовать из-за реализации, предоставляемой целочисленным типом: соответствие минимальным требованиям unsigned char. Что касается ... гарантированно будет typedef ... похоже, хороший вопрос для публикации. - person chux - Reinstate Monica; 28.02.2018