Подтвердить что ты не робот

Худшие побочные эффекты от подписки на символы. (Объяснение эффектов подписи на символах и приведениях)

Я часто работаю с библиотеками, которые используют char при работе с байтами на С++. Альтернативой является определение байта как unsigned char, но это не тот стандарт, который они решили использовать. Я часто передаю байты из С# в С++ dll и отсылаю их к char для работы с библиотекой.

При нажатии ints на символы или символы другим простым типам некоторые из побочных эффектов могут возникать. В частности, когда у вас есть этот сломанный код, над которым вы работали, и как вы узнали, что это связано с подписью char?

Lucky, я не столкнулся с этим в своем коде, использовал char подписанный трюк для кастинга во встроенном классе систем в школе. Я ищу, чтобы лучше понять этот вопрос, поскольку считаю, что это относится к работе, которую я делаю.

4b9b3361

Ответ 1

Одним из основных факторов риска является необходимость сдвига байтов. Подписанный char хранит знаковый бит при правом смещении, тогда как без знака char нет. Здесь небольшая тестовая программа:

#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 или signed char.

Ответ 2

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

Например, при реализации telnet вы можете сделать это.

// Check for IAC (hex FF) byte
if (ch == 0xFF)
{
    // ...

Или при тестировании многобайтовых последовательностей UTF-8.

if (ch >= 0x80)
{
    // ...

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

Ответ 3

Тот, который меня больше всего раздражает:

typedef char byte;

byte b = 12;

cout << b << endl;

Конечно, это косметика, но arrr...

Ответ 4

Я был укушен знаком char в написании алгоритмов поиска, которые использовали символы из текста в качестве индексов в деревьях состояний. У меня также возникли проблемы при расширении символов в более крупные типы, и бит знака распространяется, вызывая проблемы в другом месте.

Я узнал, когда начал получать причудливые результаты, и segfaults, возникающие в результате поиска текстов, отличных от тех, которые я использовал во время начальной разработки (очевидно, что символы со значениями > 127 или 0 будут вызывать это и выиграть Обязательно присутствуйте в типичных текстовых файлах.

Всегда проверяйте переменную подписи при работе с ней. В общем, теперь я делаю типы подписанными, если у меня нет веских причин, иначе, при необходимости, при необходимости. Это хорошо вписывается в вездесущее использование char в библиотеках, чтобы просто представлять байт. Имейте в виду, что подпись char не определена (в отличие от других типов), вы должны уделять ей особое внимание и быть внимательным.

Ответ 5

При запуске ints для символов или символов другим простым типам

Критическая точка заключается в том, что приведение знакового значения из одного примитивного типа в другой (более крупный) тип не сохраняет битовый шаблон (предполагая два дополнения). Подписанный char с битовой схемой 0xff равен -1, а подписанный короткий с десятичным значением -1 - 0xffff. Однако выдача без знака char со значением 0xff в unsigned short, дает 0x00ff. Поэтому всегда думайте о правильной подписке перед тем, как придать тип более крупному или меньшему типу данных. Никогда не носите неподписанные данные в подписанных типах данных, если вам не нужно - если внешняя библиотека заставляет вас это делать, сделайте преобразование как можно позже (или как можно раньше, если внешний код выступает в качестве источника данных).

Ответ 6

Вы будете терпеть неудачу при компиляции для нескольких платформ, потому что стандарт С++ не определяет char как определенную "подпись".

Поэтому GCC вводит опции -fsigned-char и -funsigned-char для принудительного выполнения определенного поведения. Например, здесь можно найти здесь.

EDIT:

Как вы просили примеры неработающего кода, существует множество возможностей для разрыва кода, обрабатывающего двоичные данные. Например, изображение обрабатывает 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);

Надеюсь, вам понравится этот пример;-) Но, честно говоря, я никогда не сталкивался с такими проблемами, даже будучи новичком, насколько я помню...

Надеюсь, что этого ответа достаточно для вас. Как насчет короткого комментария?

Ответ 7

Спецификации языка C и С++ определяют 3 типа данных для хранения символов: char, signed char и unsigned char. Последние 2 обсуждались в других ответах. Посмотрим на тип char.

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

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

Ответ 8

Расширение знака. Первая версия моей функции кодирования URL-адресов создала строки типа "% FFFFFFA3".