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