Должен ли буфер из байтов быть подписанным или беззнаковым буфером символов?

StackOverflow https://stackoverflow.com/questions/653336

  •  19-08-2019
  •  | 
  •  

Вопрос

Должен ли буфер из байтов быть подписанным символом char или беззнаковым символом char или просто буфером char?Есть какие-нибудь различия между C и C ++?

Спасибо.

Это было полезно?

Решение

Должен ли буфер из байтов быть подписан char или беззнаковый char или просто char буфер?Есть ли различия между C и C ++?

Незначительная разница в том, как язык трактует это.A Огромный разница в том, как к этому относится конвенция.

  • char = ASCII (или UTF-8, но там мешает знаковость) текстуальный данные
  • unsigned char = байт
  • signed char = редко используется

И есть код, который полагается о таком различии.Всего неделю или две назад я столкнулся с ошибкой, из-за которой данные JPEG повреждались из-за того, что они передавались в char* версия нашей функции кодирования Base64— которая "услужливо" заменила все недопустимые UTF-8 в "строке".Переход на BYTE ака unsigned char это было все, что потребовалось, чтобы это исправить.

Другие советы

Если вы намерены хранить произвольные двоичные данные, вам следует использовать unsigned char.Это единственный тип данных, который гарантированно не содержит битов заполнения по стандарту C.Каждый другой тип данных может содержать биты заполнения в своем объектном представлении (то есть тот, который содержит все биты объекта, а не только те, которые определяют значение).Состояние битов заполнения не указано и не используется для хранения значений.Так что, если вы читаете, используя char некоторые двоичные данные, все было бы сокращено до диапазона значений символа (путем интерпретации только битов значений), но все еще могут быть биты, которые просто игнорируются, но все еще существуют и считываются memcpy.Очень похоже на биты заполнения в реальных структурных объектах.Тип unsigned char гарантированно не содержит таковых.Это следует из 5.2.4.2.1/2 (C99 TC2, здесь n1124):

Если значение объекта типа char обрабатывается как целое число со знаком при использовании в выражении , значение CHAR_MIN должен быть таким же, как у SCHAR_MIN и ценность CHAR_MAX должен быть таким же, как у SCHAR_MAX.В противном случае значение CHAR_MIN должно быть равно 0, а значение CHAR_MAX должен быть таким же, как у UCHAR_MAX. Значение UCHAR_MAX должны равняться 2^CHAR_BIT − 1

Из последнего предложения следует, что не осталось места ни для каких битов заполнения.Если вы используете char что касается типа вашего буфера, то у вас также есть проблема с переполнениями:Явное присвоение любого значения одному такому элементу , который находится в диапазоне 8 бит - таким образом, вы можете ожидать, что такое присвоение будет в порядке вещей, но не в пределах диапазона char, который является CHAR_MIN..CHAR_MAX, такое преобразование переполняется и приводит к реализации определенных результатов, включая повышение уровня сигналов.

Даже если какие-либо проблемы, связанные с вышеизложенным, вероятно, не проявились бы в реальных реализациях (было бы очень низкое качество реализации), вам лучше всего с самого начала использовать правильный тип, который unsigned char.

Однако для строк выбираемым типом данных является char, который будет пониматься функциями string и print.Используя signed char для этих целей мне кажется неправильным решением.

Для получения дополнительной информации читайте this proposal которые содержат исправление для следующей версии стандарта C, которая в конечном итоге потребует signed char также не имеет никаких дополнительных элементов.Это уже включено в рабочий документ.

Это зависит от обстоятельств.

Если буфер предназначен для хранения текста, то, вероятно, имеет смысл объявить его как массив char и пусть платформа решит за вас, является ли это подписанным или неподписанным по умолчанию.Это доставит вам меньше всего проблем с передачей данных, например, в библиотеку времени выполнения реализации и из нее.

Если буфер предназначен для хранения двоичных данных, то это зависит от того, как вы собираетесь его использовать.Например, если двоичные данные действительно представляют собой упакованный массив выборок данных, которые являются подписанными 8-разрядными измерениями АЦП с фиксированной точкой, то signed char было бы лучше всего.

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

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

Вы должны использовать либо обугливающийся или неподписанный символ но никогда подписанный символ.Стандарт содержит следующее в разделе 3.9/2

Для любого объекта (кроме подобъекта базового класса) типа POD T, независимо от того, содержит объект или нет допустимое значение типа T, базовые байты (1.7), составляющие объект, могут быть скопирован в массив char или беззнаковый char.Если содержимое массива char или unsigned char копируется обратно в объект, впоследствии объект должен сохранить свое исходное значение.

Лучше определить его как unsigned char .Фактически тип Win32 BYTE определяется как unsigned char (символ без знака).Между C и C ++ нет никакой разницы в этом.

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

Выбор int8_t против uint8_t аналогичен тому, когда вы сравниваете ptr с нулевым значением.


С точки зрения функциональности, сравнение с NULL - это то же самое, что сравнение с 0, потому что NULL - это #define для 0.

Но лично, с точки зрения стиля кодирования, я предпочитаю сравнивать свои указатели с NULL, потому что NULL #define обозначает человека, поддерживающего код, который вы проверяете на наличие неверного указателя...

против

когда кто-то видит сравнение с 0, это означает, что вы проверяете определенное значение.


По вышеуказанной причине я бы использовал uint8_t.

Если вы извлекаете элемент в более широкую переменную, он, конечно, будет расширен по знаку или нет.

Должен и обязанный ...Я склонен к предпочитаю без подписи, поскольку она кажется более "сырой", менее привлекательной для того, чтобы сказать: "эй, это просто куча мелких ints", если я хочу подчеркнуть двоичность данных.

Я не думаю, что я когда-либо использовал явное signed char для представления буфера из байтов.

Конечно, один третий вариант заключается в представлении буфера в виде void * как можно больше.Многие распространенные функции ввода-вывода работают с void *, поэтому иногда решение о том, какой целочисленный тип использовать, может быть полностью инкапсулировано, что приятно.

Несколько лет назад у меня была проблема с консольным приложением C ++, которое печатало цветные символы для значений ASCII выше 128, и это было решено переключением с char на unsigned char, но я думаю, что это было решаемо и при сохранении типа char.

На данный момент большинство функций C / C ++ используют char, и теперь я понимаю оба языка намного лучше, поэтому в большинстве случаев использую char.

Тебе действительно не все равно?Если вы этого не сделаете, просто используйте значение по умолчанию (char) и не загромождайте свой код неважными деталями.В противном случае будущие сопровождающие будут задаваться вопросом, почему вы использовали signed (или unsigned).Сделайте их жизнь проще.

Если вы солжете компилятору, он вас накажет.

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

Однако, если вам приходится оперировать содержимым буфера, то правильное объявление типа упростит ваш код.Никакой ерунды типа "int val = buf[i] & 0xff;".

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

typedef char byte;

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

Я знаю, это несколько глупо, но это позволяет вашему коду читаться на 100% так, как вы задумали.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top