在 C++ 中将十六进制字符串转换为无符号字符

Convert hex string into unsigned char in 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 位。

有人可以向我解释为什么会发生这种情况以及如何解决它吗?

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 The typedef name intN_t designates a signed integer type with width N, no padding bits, and a two's complement representation. Thus, int8_t denotes a signed integer type with a width of exactly 8 bits.

2 The typedef name uintN_t designates an unsigned integer type with width N. Thus, uint24_t denotes an unsigned integer type with a width of exactly 24 bits.

3 These types are optional. However, if an implementation provides integer types with widths of 8, 16, 32, or 64 bits, it shall define the corresponding typedef names.

这件事的背景是历史。曾几何时,存在一个字节没有 8 位的平台,例如许多 PDP(更不用说早期的 UNIVACs1 等十进制计算机)。今天我们很少对这些感兴趣,但在设计 C 时它们很重要,因此,如果今天开发 C,可能会做出某些假设,但 C 标准中没有。

在这些平台上,无法始终轻松提供 8 位整数类型,并且 unsigned char 被定义为恰好一个字节宽,如果一个字节是不是 8 位宽。这一点,连同其他一些东西 2,就是为什么所有 uintN_t 类型都是可选的,也是为什么其中 none 与特定整数类型相关联的原因。目的是定义提供特定低级行为的类型。如果实现不能提供那种行为,至少它会出错而不是编译废话。

因此,完全迂腐:如果您完全使用 uint8_t,则可以编写一个完全拒绝您的代码的符合标准的 C++ 实现。也可以编写一个符合规范的实现,其中 uint8_t 是一个不同于 unsigned char 的整数类型,其中问题中的代码可以正常工作。

然而,实际上,您不太可能遇到这样的实现。我所知道的所有当前 C++ 实现都将 uint8_t 定义为 unsigned char.3

的别名

1 即使那还不是兔子洞的深度,尽管我怀疑 C 语言的创造者是否考虑过 Setun(俄罗斯平衡三元计算机)。

2 例如,并非所有这些机器都将整数表示为二进制补码。

3如果您知道没有的,请发表评论,我会在此处记录下来。我想可能有一个微控制器工具包有理由偏离。