繁体   English   中英

C ++流负数转换

[英]c++ stream negative number conversion

我遇到了一个C ++的问题,它试图读取一个以十六进制形式填充有符号整数的文本文件,并将其解析为向量。 我使用C ++流对变量进行重定向(流>> var),并且似乎不能正确解析负数-变量的值为0,并且设置了流失败标志。

如果我尝试使用strtol()函数转换字符串,则结果符合预期。 同样,如果我尝试先将流重定向到无符号整数,然后将变量强制转换为有符号整数,则结果将再次正确,并且不会报告任何流错误。

我在Xeon E5-2643 v3系统上运行的Debian 9.1(x64)上使用gcc 6.3.0。

还有其他人遇到此问题吗? 我希望转换与strtol函数的工作方式相同,并且不会报告任何流错误。 我是否缺少某些流设置/忘记在此处调用某些功能或设置一些标志?

任何建议将不胜感激。

下面随附的是演示此问题的示例C ++程序。

#include <iostream>
#include <sstream>
#include <cstdio>
#include <cstdlib>
#include <cstdint>


int main()
{
  const char* minus_one = "0xffffffff";

  std::stringstream ss;
  ss << minus_one;

  std::cout << "input string    : " << ss.str() << "\n"; // outputs "0xffffffff"

  // C-style conversion
  int32_t cint;
  cint = strtol(ss.str().c_str(), NULL, 0);
  std::cout << "strtol conv     : " << cint <<  " (" << std::hex << cint << ")\n"; // outputs "-1 (ffffffff)"
  std::cout << std::dec;

  // C++-style conversion
  int32_t cppint;
  ss >> std::hex >> cppint;
  std::cout << std::dec << "ssextr conv     : " << cppint <<  " (" << std::hex << cppint << ")\n"; // outputs "0 (0)" <- ERROR
  std::cout << std::dec;
  if (ss.fail()) std::cout << "Error converting number.\n";

  // C++-style conversion with cast
  uint32_t cppuint;
  int32_t cppint2;
  ss.clear();
  ss.str(minus_one);
  ss >> std::hex >> cppuint;
  cppint2 = (int32_t)cppuint;
  std::cout << std::dec << "ssextr cast conv: " << cppint2 <<  " (" << std::hex << cppint2 << ")\n"; // outputs "-1 (0xffffffff)"
  std::cout << std::dec;
  if (ss.fail()) std::cout << "Error converting number.\n";

  exit(EXIT_SUCCESS);
}
int32_t cint;
cint = strtol(ss.str().c_str(), NULL, 0);

这会将值0xffffffff读取为long ,然后将其转换为int32_t 如果long大于32位,则strtol起作用并返回0xffffffff即4294967295,并将其转换为int32_t会产生-1。 但这与从字符串中读取负数不同(如果long是32位,那么它将无法按预期工作,而是返回LONG_MAX并将其转换为int32_t ,即0x7fffffff )。

int32_t cppint;
ss >> std::hex >> cppint;

这试图将值0xffffffff读取到int32_t但是值0xffffffff不适用于该类型,因此读取值失败(就像long 32位时strtol失败一样)。

与您的strtol版本更接近的是:

int32_t cppint;
long l;
if (ss >> std::hex >> l)
  cppint = l;
else
  // handle error ...

期望能够将值0xffffffff读取为带符号的32位整数是不合理的。 strtol和istream不读取位模式,它们读取数字,并且数字0xffffffff不适合有符号的32位整数。

如果第一个十六进制位是f ,则c ++将其设为“大数”:“ 0x7fffffff”。 似乎c ++不想将其表示为负数。 像这样:

const char* minus_one = "0xf0000000";   //ssextr conv     : 2147483647 (7fffffff)
std::stringstream ss;
ss << minus_one;

// C++ style conversion
int32_t cppint;
ss >> std::hex >> cppint;
std::cout << std::dec << "ssextr conv     : " << cppint <<  " (" << std::hex << cppint << ")\n"; 
std::cout << std::dec;
if (ss.fail()) {
    std::cout << "Error converting number.\n";
}

问题在于,十六进制表示法记录了无符号整数。 strtol是一个C函数,显然对负整数的十六进制表示形式更宽容,并且在内部将字符串读取为无符号值,然后将其重新解释为有符号值。 但是即使在C语言中,也没有为strtol指定这种处理,也没有为无法以有符号类型表示的无符号值的转换指定结果,要么是实现定义的结果,要么是实现定义的信号的产生 (摘自C11 6.3.1.3 [Conversions]有符号和无符号整数的1570草案)

为了避免破坏大量遗留代码,它可能以这种方式工作,但是C ++是一种较新的语言,实现者已决定对十六进制表示法进行更严格的规定。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM