Я часто работаю с библиотеками, которые используют символ при работе с байтами в C++. Альтернатива должна определить "Байт" как неподписанный символ, но что не стандарт они решили использовать. Я часто передаю байты от C# в C++ dlls и бросаю их для обугливания для работы с библиотекой.
При кастинге ints к символам или символам к другим простым типам, что является некоторыми побочными эффектами, которые могут произойти. А именно, когда это взломало код, что Вы продолжили работать и как Вы узнавали, что это было из-за символа, со знаком?
Удачный я не столкнулся с этим в своем коде, использовали символ, подписанный, бросая прием назад в классе встроенных систем в школе. Я надеюсь лучше понимать проблему, так как я чувствую, что это относится к работе, которую я делаю.
Один из основных рисков заключается в том, что вам нужно сдвинуть байты. Знаковый символ со знаком сохраняет бит знака при сдвиге вправо, тогда как знак без знака - нет. Вот небольшая тестовая программа:
#include <stdio.h>
int main (void)
{
signed char a = -1;
unsigned char b = 255;
printf("%d\n%d\n", a >> 1, b >> 1);
return 0;
}
Она должна вывести -1 и 127, даже если a и b начинаются с тем же битовым шаблоном (заданы 8-битные символы, два дополнения и значения со знаком с использованием арифметического сдвига).
Короче говоря, вы не можете полагаться на то, что сдвиг работает одинаково для подписанных и беззнаковых символов, поэтому, если вам нужна переносимость, используйте unsigned char
, а не char
или подписанный. char
.
Меня укусила символьная подпись при написании алгоритмов поиска, которые использовали символы из текста в качестве индексов в деревьях состояний. У меня также были проблемы с расширением символов в более крупные типы, а знаковый бит распространяется, вызывая проблемы в других местах.
Я узнал об этом, когда начал получать странные результаты и ошибки сегментации, возникающие при поиске текстов, отличных от того, который я использовал во время начальной разработки (очевидно, символы со значениями> 127 или <0 будут вызывать это, и победил ' t обязательно должны присутствовать в ваших типичных текстовых файлах.
Всегда проверяйте наличие подписи переменной при работе с ней. Обычно теперь я делаю подписанные типы, если у меня нет веской причины в противном случае, выполняя приведение при необходимости. char
в библиотеках, чтобы просто представить байт. Помните, что подписи char
не определены (в отличие от других типов), вы должны уделить ему особое внимание и быть внимательными.
Спецификации языков C и C ++ определяют 3 типа данных для хранения символов: char
, signed char
и символ без знака
. Последние 2 обсуждались в других ответах. Давайте посмотрим на тип char
.
Стандарт (стандарты) говорят, что тип данных char
может быть подписанным или без знака и является решением реализации. Это означает, что некоторые компиляторы или версии компиляторов могут реализовать char
по-разному. Подразумевается, что тип данных char
не подходит для арифметических или логических операций. Для арифметических и логических операций подписанные
и беззнаковые
версии char
будут работать нормально.
Таким образом, существует 3 версии типа данных char
. Тип данных char
хорошо подходит для хранения символов, но не подходит для арифметики на разных платформах и трансляторах, поскольку его подписи определяется реализацией.
Самые очевидные гетчи приходят тогда, когда при реализации протоколов или схем кодирования необходимо сравнить числовое значение char
с шестнадцатеричной константой.
Например, при реализации telnet это может понадобиться.
// Check for IAC (hex FF) byte
if (ch == 0xFF)
{
// ...
Или при тестировании для многобайтовых последовательностей UTF-8.
if (ch >= 0x80)
{
// ...
К счастью, эти ошибки, как правило, не сохраняются до тех пор, пока даже самое поверхностное тестирование на платформе со знаком char
должно их выявить. Их можно исправить, используя символьную константу, преобразовав числовую константу в char
или преобразовав символ в неподписанный char
до того, как оператор сравнения продвинет и ту, и другую в int
. Однако прямое преобразование символа char
в unsigned
не сработает.
if (ch == '\xff') // OK
if ((unsigned char)ch == 0xff) // OK, so long as char has 8-bits
if (ch == (char)0xff) // Usually OK, relies on implementation defined behaviour
if ((unsigned)ch == 0xff) // still wrong
Тот, который меня больше всего раздражает:
typedef char byte;
byte b = 12;
cout << b << endl;
Конечно, это косметика, но ...
При приведении целых чисел к символам или символов к другим простым типам
Критическим моментом является то, что преобразование значения со знаком из одного примитивного типа в другой (больший) тип не сохраняет битовый шаблон (при условии дополнения до двух). Знаковый символ с битовой комбинацией 0xff
равен -1, а знаковый короткий с десятичным значением -1 равен 0xffff
. Однако преобразование беззнакового char со значением 0xff
в беззнаковое короткое приводит к 0x00ff
. Поэтому всегда думайте о правильной подписи, прежде чем приводить к большему или меньшему типу данных. Никогда не переносите неподписанные данные в подписанных типах данных, если вам не нужно - если внешняя библиотека вынуждает вас сделать это, выполняйте преобразование как можно позже (или как можно раньше, если внешний код действует в качестве источника данных).
Вы потерпите неудачу при компиляции для нескольких платформ, потому что стандарт C ++ не определяет char
как имеющий определенную «подписанность».
Поэтому GCC вводит параметры -fsigned-char
и -funsigned-char
для принудительного выполнения определенного поведения. Дополнительную информацию по этой теме можно найти, например, здесь .
РЕДАКТИРОВАТЬ:
Как вы просили привести примеры неработающего кода, существует множество возможностей взломать код, обрабатывающий двоичные данные. Например, для изображения вы обрабатываете 8-битные образцы звука (диапазон от -128 до 127) и хотите уменьшить громкость вдвое. А теперь представьте этот сценарий (в котором наивный программист предполагает char == signed char
):
char sampleIn;
// If the sample is -1 (= almost silent), and the compiler treats char as unsigned,
// then the value of 'sampleIn' will be 255
read_one_byte_sample(&sampleIn);
// Ok, halven the volume. The value will be 127!
char sampleOut = sampleOut / 2;
// And write the processed sample to the output file, for example.
// (unsigned char)127 has the exact same bit pattern as (signed char)127,
// so this will write a sample with the loudest volume!!
write_one_byte_sample_to_output_file(&sampleOut);
Надеюсь, вам понравится этот пример ;-) Но, честно говоря, я никогда не сталкивался с такими проблемами, даже как новичок, насколько я помню ...
Надеюсь, этого ответа достаточно для вас, проголосовавших против. А как насчет короткого комментария?
Расширение знака. Первая версия моей функции кодирования URL выдавала строки вроде «% FFFFFFA3».