Должен ли быть буфер байтов со знаком или беззнаковый буфер символов? - PullRequest
53 голосов
/ 17 марта 2009

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

Спасибо.

Ответы [ 14 ]

47 голосов
/ 17 марта 2009

Если вы намереваетесь хранить произвольные двоичные данные, вы должны использовать 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, который будет понятен для функций строк и печати. Использование signed char для этих целей кажется мне неправильным решением.

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

33 голосов
/ 20 февраля 2011

Если буфер байтов будет подписан символ или без знака или просто символ буфер? Любые различия между C и C ++? * * 1002

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

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

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

12 голосов
/ 17 марта 2009

Это зависит.

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

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

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

9 голосов
/ 17 марта 2009

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

5 голосов
/ 17 марта 2009

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

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

4 голосов
/ 17 марта 2009

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

3 голосов
/ 17 марта 2009

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

2 голосов
/ 17 марта 2009

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


С функциональной точки зрения сравнение с NULL аналогично сравнению с 0, поскольку NULL является #define для 0.

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

VS

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


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

0 голосов
/ 17 марта 2009
typedef char byte;

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

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

0 голосов
/ 17 марта 2009

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

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

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

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

...