在C++中将十六进制字符串转换为unsigned char

mor*_*rph 4 c++ string hex stringstream

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

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

显然,我假设转换会导致x = 0x1f(十进制= 31),因为0x1f小于0xff,这是可以存储在8位无符号字符中的最大值.相反的是,在转换中只使用了我的字符串的前8位.

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

Win*_*ute 7

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.
Run Code Online (Sandbox Code Playgroud)

迂腐附录(主要是历史利益)

如果我们是完全迂腐的,那么uint8_t是一个可选的(!)实现定义的无符号整数类型,如果它存在则恰好是8位宽.C++将定义推迟到[cstdint.syn]/2中的C标准,C99在7.18.1.1中定义:

1 typedef名称intN_t指定有符号整数类型,其宽度为N,无填充位和二进制补码表示.因此,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(一种俄罗斯平衡三元计算机).

例如,并非所有那些机器都将整数表示为两个补码.

3如果您知道某个没有,请发表评论,我会在此处记下.我想有一个微控制器工具包可能有偏差的原因.