Преобразование шестнадцатеричной строки в беззнаковый символ в C++

Я хочу преобразовать шестнадцатеричное представление в строке в переменную без знака, например:

std::stringstream ss;
uint8_t x;
ss << "1f";
ss >> std::hex >> x;  // result: x = 0x31 (=49 in decimal and ='1' as char)

Очевидно, я предположил, что преобразование приведет к x = 0x1f (=31 в десятичном виде), так как 0x1f меньше 0xff, что является максимумом, который может быть сохранен в 8-битном неподписанном символе. Вместо этого произошло то, что в преобразовании использовались только первые 8 бит моей строки.

Может кто-нибудь объяснить мне, почему именно это произошло и как это исправить?

1 ответ

Решение

std::uint8_t является (как правило, см. ниже) псевдонимом для unsigned charи соответствующийoperator>> трактует его как тип символа, а не как целочисленный тип. Из-за этого персонаж '1' считывается в x, и его значение ASCII равно 49. Что шестнадцатеричная запись значения ASCII '1' случается, что десятичная запись значения, которое вы хотите проанализировать, является случайной; пытаясь разобрать "1e" или же "10" или же "1xyz" все равно приведет к x == 49,

Чтобы обойти эту проблему, проанализируйте сначала другой целочисленный тип, затем сузьте до 8 бит:

std::stringstream ss;
uint8_t x;
unsigned tmp;

ss << "1f";
ss >> std::hex >> tmp; 
x = tmp;                // may need static_cast<uint8_t>(tmp) to suppress
                        // compiler warnings.

Педантичное приложение (в основном исторического интереса)

Если мы совершенно педантичны, uint8_t является необязательным (!) определяемым реализацией беззнаковым целочисленным типом, который имеет ширину ровно 8 битов, если он существует. C++ переводит определение в стандарт C в [cstdint.syn]/2, а C99 определяет в 7.18.1.1:

1 имя типа определения intN_t обозначает целочисленный тип со знаком с шириной N, без дополнительных битов и представление дополнения до двух. Таким образом, int8_t обозначает целочисленный тип со знаком шириной ровно 8 бит.

2 Имя типа определения uintN_t обозначает целочисленный тип без знака с шириной N. Таким образом, uint24_t обозначает целочисленный тип без знака с шириной ровно 24 бита.

3 Эти типы не являются обязательными. Однако, если реализация предоставляет целочисленные типы с шириной 8, 16, 32 или 64 бита, она должна определить соответствующие имена typedef.

Фон для этого - история. Когда-то существовали платформы, на которых в байте не было 8 битов, например, несколько PDP (не говоря уже о десятичных компьютерах, таких как ранние UNIVACs1). Они редко представляют интерес для нас сегодня, но они были важны при разработке C, и, как следствие, определенные предположения, которые могли бы быть сделаны, если бы C были разработаны сегодня, не делаются в стандарте C.

На этих платформах не всегда легко обеспечить 8-битные целочисленные типы, и unsigned charбудучи определенным точно как один байт шириной, не может в то же самое время быть точно 8 битами шириной, если байт не имеет ширины 8 битов. Это, наряду с несколькими другими вещами,2, поэтому все uintN_t типы являются необязательными, а также почему ни один из них не привязан к конкретным целочисленным типам. Намерение состояло в том, чтобы определить типы, которые предлагают определенное низкоуровневое поведение. Если реализация не может обеспечить такое поведение, по крайней мере, она выдаст ошибку, а не скомпилирует ерунду.

Итак, будучи совершенно педантичным: если вы используете uint8_t В общем, можно написать соответствующую реализацию C++, которая полностью отклоняет ваш код. Также возможно написать соответствующую реализацию, в которой uint8_t целочисленный тип, отличный от unsigned charгде код в вопросе просто работает.

Однако на практике вы вряд ли столкнетесь с такой реализацией. Все текущие реализации C++, о которых я знаю, определяют uint8_t как псевдоним unsigned char,3

1 И даже это не глубина кроличьей норы, хотя я сомневаюсь, что создатели C имели в виду Setun (русский сбалансированный троичный компьютер).

Например,2 не все эти машины представляли целые числа как дополнение к двум.

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

Другие вопросы по тегам