Это действительно необходимо использовать unsigned char
хранить двоичные данные, как в некоторых библиотеках, которые работают с кодировкой символов или двоичными буферами? Чтобы понять мой вопрос, взгляните на код ниже:
char c[5], d[5];
c[0] = 0xF0;
c[1] = 0xA4;
c[2] = 0xAD;
c[3] = 0xA2;
c[4] = '\0';
printf("%s\n", c);
memcpy(d, c, 5);
printf("%s\n", d);
оба printf's
выход правильно, где
f0 a4 ad a2
кодировка для кодовой точки Unicode U+24B62 ()
в шестнадцатеричном виде
Четное memcpy
также правильно скопировал биты, хранящиеся в символе.
Какие рассуждения могут способствовать использованию unsigned char
вместо plain char
?
По другим связанным вопросам unsigned char
выделен, потому что это единственный (байтовый / наименьший) тип данных, который гарантированно не будет заполнен C-спецификацией. Но, как показано в приведенном выше примере, на вывод не влияют никакие дополнения как таковые.
Я использовал VC ++ Express 2010 и MinGW для компиляции выше. Хотя ВК дал предупреждение
warning C4309: '=' : truncation of constant value
вывод не отражает это.
Постскриптум Это может быть отмечено возможным дубликатом Должен ли быть буфер байтов со знаком или без знака в буфере символов? но мое намерение другое. Я спрашиваю, почему то, что, кажется, работает так хорошо с char
должен быть напечатан unsigned char
?
Обновить: Цитировать из N3337,
Section 3.9 Types
2 Для любого объекта (кроме подобъекта базового класса) тривиально
копируемый тип T, содержит ли объект допустимое значение типа
T, лежащие в основе байты (1.7), составляющие объект, могут быть скопированы в
массив char или unsigned char. Если содержимое массива char
или беззнаковый символ копируется обратно в объект, объект должен
впоследствии сохраните свое первоначальное значение.
Ввиду вышеуказанного факта и того, что мой оригинальный пример был на машине Intel, где char
по умолчанию signed char
Я до сих пор не уверен, если unsigned char
должно быть предпочтительнее, чем char
,
Что-нибудь еще?
В С unsigned char
тип данных является единственным типом данных, который имеет все следующие три свойства одновременно
если это свойства «двоичного» типа данных, который вы ищете, вам обязательно следует использовать unsigned char
,
Для второго свойства нам нужен тип, который unsigned
, Для них все преобразования определяются по модулю arihmetic, здесь по модулю UCHAR_MAX+1
, 256
в большинстве 99% архитектур. Все преобразования более широких значений в unsigned char
тем самым просто соответствует усечению до младшего байта.
Два других типа символов обычно не работают одинаково. signed char
во всяком случае, подписано, поэтому преобразование значений, которые ему не подходят, не очень хорошо определено. char
не фиксируется как подписанный или неподписанный, но на конкретной платформе, на которую переносится ваш код, он может быть подписан, даже если он не подписан на вашей.
Простой char
Тип проблематичен и не должен использоваться ни для чего, кроме строк. Основная проблема с char
в том, что вы не можете знать, подписан он или нет, это поведение, определяемое реализацией. Это делает char
отличный от int
так далее, int
всегда гарантированно будет подписано.
Хотя ВК дал предупреждение … усечение константы
Он говорит вам, что вы пытаетесь хранить литералы int внутри переменных типа char. Это может быть связано со подписью: если вы попытаетесь сохранить целое число со значением> 0x7F внутри подписанного символа, могут произойти непредвиденные ситуации. Формально, это неопределенное поведение в C, хотя практически вы просто получите странный вывод, если попытаетесь напечатать результат в виде целочисленного значения, хранящегося в (подписанном) символе.
В этом конкретном случае предупреждение не должно иметь значения.
РЕДАКТИРОВАТЬ :
В других связанных вопросах неподписанный символ выделяется, потому что это единственный (байтовый / наименьший) тип данных, который гарантированно не будет заполнен Си-спецификацией.
Теоретически, все целочисленные типы, кроме беззнакового символа и знакового символа, могут содержать «биты заполнения», согласно C11 6.2.6.2:
«Для целочисленных типов без знака, кроме беззнаковых символов, биты
представление объекта должно быть разделено на две группы: биты значения и
биты заполнения (не должно быть ни одного из последних). ««Для целочисленных типов со знаком биты представления объекта должны
разделить на три группы: биты значения, биты заполнения и знак
немного. Там не должно быть никаких битов заполнения; подписанный чар не должен иметь
любые биты заполнения. «
Стандарт C намеренно расплывчатый и нечеткий, что позволяет использовать эти теоретические биты заполнения, потому что:
Однако в реальном мире за пределами стандарта C применяется следующее:
Таким образом, нет никакой реальной причины использовать неподписанный символ или подписанный символ только для того, чтобы избежать теоретического сценария в стандарте C.
Вы получите большинство своих проблем при сравнении содержимого отдельных байтов:
char c[5];
c[0] = 0xff;
/*blah blah*/
if (c[0] == 0xff)
{
printf("good\n");
}
else
{
printf("bad\n");
}
может вывести «bad», потому что, в зависимости от вашего компилятора, c [0] будет расширяться до -1, что совсем не то же самое, что 0xff
Байты обычно предназначены как 8-битные целые числа без знака.
Теперь char не указывает знак целого числа: на некоторых компиляторах char может быть подписан, на других он может быть без знака.
Если я добавлю операцию сдвига в код, который вы написали, то у меня будет неопределенное поведение. Добавленное сравнение также будет иметь неожиданный результат.
char c[5], d[5];
c[0] = 0xF0;
c[1] = 0xA4;
c[2] = 0xAD;
c[3] = 0xA2;
c[4] = '\0';
c[0] >>= 1; // If char is signed, will the 7th bit go to 0 or stay the same?
bool isBiggerThan0 = c[0] > 0; // FALSE if char is signed!
printf("%s\n", c);
memcpy(d, c, 5);
printf("%s\n", d);
Что касается предупреждения во время компиляции: если char подписан, то вы пытаетесь присвоить значение 0xf0, которое не может быть представлено в подписанном char (в диапазоне от -128 до +127), поэтому оно будет приведено к значению со знаком (- 16).
Объявление char как подписанного уберет предупреждение, и всегда хорошо иметь чистую сборку без какого-либо предупреждения.
Подпись равнины char
Тип определяется реализацией, поэтому, если вы на самом деле не имеете дело с символьными данными (строка, использующая набор символов платформы — обычно ASCII), обычно лучше явно указывать подпись, используя либо signed char
или же unsigned char
,
Для двоичных данных наилучший выбор наиболее вероятен unsigned char
особенно, если с данными будут выполняться побитовые операции (в частности, сдвиг битов, который для подписанных типов ведет себя не так, как для неподписанных типов).
Я спрашиваю, почему то, что, кажется, работает нормально с char, должно быть напечатано без знака char?
Если вы делаете вещи, которые не являются «правильными» в смысле стандарта, вы полагаетесь на неопределенное поведение. Ваш компилятор может сделать это так, как вы хотите сегодня, но вы не знаете, что он сделает завтра. Вы не знаете, что делает GCC или VC ++ 2012. Или даже если поведение зависит от внешних факторов или отладки / выпуска и т. Д. Как только вы выходите из безопасного пути стандарта, вы можете столкнуться с проблемами.
Ну, что вы называете «двоичными данными»? Это набор битов, без какого-либо значения, присваиваемого им той конкретной частью программного обеспечения, которая называет их «двоичными данными». Какой тип данных наиболее близок к примитиву, который передает идею отсутствия какого-либо конкретного значения для любого из этих битов? Я думаю unsigned char
,
Действительно ли необходимо использовать unsigned char для хранения двоичных данных, как в некоторых библиотеках, работающих с кодировкой символов или двоичными буферами?
«действительно» необходимо? Нет.
Это очень хорошая идея, и для этого есть много причин.
В вашем примере используется printf, который не является типобезопасным. То есть printf получает сигналы форматирования из строки формата, а не из типа данных. Вы могли бы так же легко попробовать:
printf("%s\n", (void*)c);
… и результат был бы таким же. Если вы попробуете то же самое с c ++ iostreams, результат будет другим (в зависимости от подписи c).
Какие рассуждения могут способствовать использованию неподписанного символа вместо простого символа?
Без знака указывает, что самый значимый бит данных (для беззнакового символа 8-й бит) представляет знак. Поскольку вам это явно не нужно, вы должны указать, что ваши данные не подписаны (бит «знак» представляет данные, а не знак других битов).