std::hex 无法处理负数?

std::hex cannot process negative numbers?

我正在尝试使用 std::hex 从文件中读取十六进制整数。

0
a
80000000
...

这些整数有正有负。

似乎std::hex不能处理负数。我不明白为什么,也没有在文档中看到定义的范围。

这是一个测试台:

#include <iostream>
#include <sstream>
#include <iomanip>

int main () {

  int i;
  std::stringstream ss;

  // This is the smallest number
  // That can be stored in 32 bits -1*2^(31)
  ss << "80000000";

  ss >> std::hex >> i;

  std::cout << std::hex << i << std::endl;

}

输出:

7fffffff

设置 std::hex 告诉流读取整数标记 as though using std::scanf with the %X formatter. %X reads into an unsigned integer,即使位模式适合,结果值也会溢出 int。由于溢出,读取失败,无法相信 i 的内容包含预期值。旁注:i 如果编译为 C++11 或更新版本或在 c++11 之前未更改其当前未指定值,则将设置为 0。

请注意,如果我们在读取后检查流状态,这是您应该经常做的事情,我们可以看到读取失败:

#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstdint> // added for fixed width integers.
int main () {

  int32_t i; //ensure 32 bit int
  std::stringstream ss;

  // This is the smallest number
  // That can be stored in 32 bits -1*2^(31)
  ss << "80000000";

  if (ss >> std::hex >> i)
  {
      std::cout << std::hex << i << std::endl;
  }
  else
  {
      std::cout << "FAIL! " <<  std::endl; //will execute this
  }
}

解决方案是,正如提问者在评论中推测的那样,读入 unsigned int(如果 int 不是 32 位,uint32_t 以避免进一步的意外)。以下是代码的零意外版本,使用 memcpy 将读取的确切位模式传输到 i.

#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstdint> // added for fixed width integers.
#include <cstring> //for memcpy
int main () {

  int32_t i; //ensure 32 bit int
  std::stringstream ss;

  // This is the smallest number
  // That can be stored in 32 bits -1*2^(31)
  ss << "80000000";

  uint32_t temp;
  if (ss >> std::hex >> temp)
  {
      memcpy(&i, &temp, sizeof(i));// probably compiles down to cast
      std::cout << std::hex << i << std::endl;
  }
  else
  {
    std::cout << "FAIL! " <<  std::endl; 
  }
}

话虽如此,暂时深入了解老派的 C 风格编码

  if (ss >> std::hex >> *reinterpret_cast<uint32_t*>(&i))
  {
    std::cout << std::hex << i << std::endl;
  }
  else
  {
    std::cout << "FAIL! " <<  std::endl; 
  }

违反了 the strict aliasing rule,但是一旦 32 位 intint32_t i; 强制使用,我会很惊讶地看到它失败了。这在最近的 C++ 标准中甚至可能是合法的,因为“类型相似”,但我仍在思考这个问题。