Должен ли быть подписан буфер байтов char или unsigned char или просто буфер char? Любые различия между C и С++?
Спасибо.
Должен ли быть подписан буфер байтов char или unsigned char или просто буфер char? Любые различия между C и С++?
Спасибо.
Если должен быть подписан буфер байтов char или без знака char или просто charбуфер? Любые различия между C и С++?
Небольшое различие в том, как язык относится к нему. A огромная разница в том, как соглашение обрабатывает его.
char
= ASCII (или UTF-8, но подпись там мешает) текстовые данныеunsigned char
= байтsigned char
= редко используетсяИ есть код, который опирается на такое различие. Всего через неделю или две назад я столкнулся с ошибкой, когда данные JPEG были повреждены, потому что они передавались в char*
версию нашей функции кодирования Base64 — который "с пользой" заменил все недопустимые UTF-8 в "строке". Переход на BYTE
aka unsigned char
- это все, что нужно для его исправления.
Если вы собираетесь хранить произвольные двоичные данные, вы должны использовать unsigned char
. Это единственный тип данных, который, как гарантируется, не имеет битов дополнений по стандарту C. Каждый другой тип данных может содержать биты заполнения в его представлении объекта (то есть тот, который содержит все биты объекта, а не только те, которые определяют значение). Состояние заполняющих битов не определено и не используется для хранения значений. Поэтому, если вы читаете с использованием char
некоторых двоичных данных, все будет сокращено до диапазона значений 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
бит, поэтому вы можете ожидать, что такое присвоение будет в порядке - но не в диапазоне a char
, который равен CHAR_MIN
.. CHAR_MAX
, такое переполнение переходов и приводит к реализации определенных результатов, включая повышение сигналов.
Даже если какие-либо проблемы, связанные с вышеизложенным, вероятно, не будут отображаться в реальных реализациях (это будет очень плохое качество реализации), лучше всего использовать правильный тип с самого начала, а это unsigned char
.
Для строк, однако, тип данных выбора char
, который будет пониматься строковыми и печатными функциями. Использование signed char
для этих целей выглядит для меня неправильным решением.
Для получения дополнительной информации прочитайте this proposal
, в котором содержится исправление для следующей версии стандарта C, которое в конечном итоге потребует signed char
not также есть любые биты дополнений. Он уже включен в рабочий документ .
Это зависит.
Если буфер предназначен для хранения текста, то, вероятно, имеет смысл объявить его как массив char
, и пусть платформа решит для вас, является ли это подписанным или неподписанным по умолчанию. Это даст вам, как минимум, проблемы с передачей данных в библиотеку времени выполнения и из нее.
Если буфер предназначен для хранения двоичных данных, это зависит от того, как вы собираетесь его использовать. Например, если двоичные данные действительно представляют собой упакованный массив выборок данных, которые подписали 8-битные измерения АЦП с фиксированной точкой, тогда лучше было бы signed char
.
В большинстве случаев в реальном мире буфер является именно этим, буфером, и вы действительно не заботитесь о типах отдельных байтов, потому что вы заполняете буфер в объемной операции, и вы собираетесь его передать выключить синтаксический анализатор для интерпретации сложной структуры данных и сделать что-то полезное. В этом случае объявите это простейшим способом.
Если на самом деле это буфер из 8 бит байтов, а не строка в стандартном языковом стандарте машины, я бы использовал uint8_t
. Не то, что существует множество машин, где char не является байтом (или байтом октета), но сделать утверждение "это буфер октетов", а не "это строка", часто является полезной документацией.
Вы должны использовать либо char, либо unsigned char, но никогда не подписываться char. Стандарт имеет следующий вид в 3.9/2
Для любого объекта (кроме подобъект базового класса) типа POD T, имеет ли объект допустимое значение типа T, лежащее в основе байты (1.7), составляющие объект, могут скопировать в массив из char или unsigned char. Если содержание массив char или без знака char равен копируется обратно в объект, объект впоследствии будет оригинальное значение.
Лучше определить его как unsigned char. Infact Win32 type BYTE определяется как unsigned char. Между этим нет разницы между C и С++.
Для максимальной переносимости всегда используйте unsigned char. Есть несколько случаев, когда это может вступить в игру. Семиализованные данные, разделяемые между системами с разными типами endian, сразу приходят на ум. При выполнении сдвига или маскировки бит значения являются другими.
Выбор int8_t vs uint8_t аналогичен выбору, когда вы сравниваете ptr как NULL.
С точки зрения функциональности, сравнивая с NULL, это то же самое, что и сравнение с 0, потому что NULL является #define для 0.
Но лично, с точки зрения стиля кодирования, я решил сравнить мои указатели с NULL, потому что NULL #define указывает на человека, поддерживающего код, который вы проверяете на плохой указатель...
VS
когда кто-то видит сравнение с 0, это означает, что вы проверяете определенное значение.
По этой причине я бы использовал uint8_t.
Если вы выберете элемент в более широкую переменную, он, конечно, будет расширен с расширением или нет.
Должен и должен... Я склонен предпочитать unsigned, так как он чувствует себя более "сырым", менее привлекательным, чтобы сказать "эй, это просто кучка маленького ints
", если я хотите подчеркнуть двоичную информацию данных.
Я не думаю, что когда-либо использовал явный signed char
для представления буфера байтов.
Конечно, один третий вариант - как можно больше представлять буфер как void *
. Многие общие функции ввода-вывода работают с void *
, поэтому иногда решение о том, какой тип целого использовать, может быть полностью инкапсулировано, что приятно.
Несколько лет назад у меня возникла проблема с консольным приложением С++, которое печатало цветные символы для значений ASCII выше 128, и это было решено путем переключения с char на unsigned char, но я думаю, что он был доступен для решения, сохраняя char тоже.
В настоящее время большинство функций C/С++ используют char, и теперь я понимаю, что оба языка намного лучше, поэтому я использую char в большинстве случаев.
Вам все равно? Если вы этого не сделаете, просто используйте значение по умолчанию (char) и не загромождайте свой код несущественным вопросом. В противном случае будущим сопровождающим будет интересно узнать, почему вы использовали подписанный (или неподписанный). Сделайте свою жизнь проще.
typedef char byte;
Теперь вы можете сделать свой массив byte
s. Это очевидно для всех, что вы имели в виду, и вы не теряете никаких функций.
Я знаю, что это несколько глупо, но это заставляет ваш код читать на 100%, как вы планировали.
Если вы лжете компилятору, он накажет вас.
Если буфер содержит данные, которые просто проходят, и вы никоим образом не будете ими манипулировать, это не имеет значения.
Однако, если вам нужно работать с содержимым буфера, то правильное объявление типа сделает ваш код более простым. Нет "int val = buf [i] и 0xff;" нонсенс.
Итак, подумайте о том, что на самом деле есть данные и как вам нужно его использовать.