我想将字符串中的十六进制表示形式转换为无符号字符变量,如下所示:

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位。
有人能给我解释一下为什么会发生这样的事情以及如何解决吗?

最佳答案:

std::uint8_tunsigned charoperator>>'1''1'"1e""10""1xyz"x == 49
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++在[CSTNDLT.SYN]/2中延迟了对C标准的定义,而C99在7.18.1.1中定义:
1 typedef name
intN_t
指定宽度为n、没有填充位和2的补码表示的有符号整数类型。因此,
int8_t
表示宽度正好为8位的有符号整数类型。
typedef name
uintN_t
指定宽度为n的无符号整数类型。因此,
uint24_t
表示宽度正好为24位的无符号整数类型。
3这些类型是可选的。但是,如果实现提供宽度为8、16、32或64位的整数类型,则应定义相应的typedef名称。
这件事的背景是历史。从前,存在着一个字节没有8位的平台,比如一些pdp(更不用说早期univacs1这样的十进制计算机了)。这些在今天很少引起我们的兴趣,但是它们在c设计时很重要,因此,如果c今天开发出来,可能会做出的某些假设并不是在c标准中做出的。
在这些平台上,8位整数类型并不总是容易提供的,并且如果一个字节不是8位宽的,则被定义为正好一个字节宽的
unsigned char
不能同时正好8位宽。这就是为什么所有
uintN_t
类型都是可选的,以及为什么它们都没有绑定到特定整数类型的原因。其目的是定义提供特定低级行为的类型。如果实现不能提供这种行为,至少它会出错,而不是编译无意义的代码。
所以,完全是迂腐的:如果你使用了
uint8_t
,就可以编写一个符合C++的实现,完全拒绝你的代码。也可以编写一个一致的实现,其中
uint8_t
是一个整数类型,不同于
unsigned char
,其中问题中的代码只起作用。
然而,在实践中,您不太可能遇到这样的实现。我知道的所有当前C++实现都定义了
uint8_t
作为
unsigned char
3的别名。
尽管我怀疑C语言的创造者是否考虑过塞顿(一种俄罗斯平衡的三元计算机),但这并不是兔子洞的深度。
2举例来说,并不是所有的机器都把整数表示为2的补码。
如果你知道其中一个不知道,请留下评论,我会在这里记录下来。我想有可能有一个微控制器工具包,有理由偏离。