我想将字符串中的十六进制表示转换为unsigned char变量,如下所示:
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位。
有人可以向我解释为什么会发生这种情况以及如何解决这个问题?
答案 0 :(得分:7)
std::uint8_t
(通常见下文)是unsigned char
的别名,corresponding operator>>
将其视为字符类型而不是整数类型。因此,字符'1'
被读入x,其ASCII值为49. '1'
的ASCII值的十六进制表示法恰好是您要解析的值的十进制表示法巧合;尝试解析"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 ++将定义推迟到[cstdint.syn] / 2中的C标准,C99在7.18.1.1中定义:
1 typedef名称
intN_t
指定有符号整数类型,宽度为 N ,没有填充位和2的补码表示。因此,int8_t
表示有符号整数类型,宽度恰好为8位。2 typedef名称
uintN_t
指定宽度为 N 的无符号整数类型。因此,uint24_t
表示无符号整数类型,宽度恰好为24位。3这些类型是可选的。但是,如果实现提供宽度为8,16,32或64位的整数类型,则应定义相应的typedef名称。
这背景是历史。曾几何时,存在一个字节没有8位的平台,例如许多PDP(更像是早期UNIVAC 1 的十进制计算机)。我们今天很少对它们感兴趣,但是在设计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 如果你知道一个没有的,请留下评论,我会在这里做一个记录。我认为可能存在偏离原因的微控制器工具包。