stringstream unsigned conversion打破了吗?

时间:2009-04-24 18:00:58

标签: c++ iostream stringstream

考虑这个程序:

#include <iostream>
#include <string>
#include <sstream>
#include <cassert>

int main()
{
    std::istringstream stream( "-1" );
    unsigned short n = 0;
    stream >> n;
    assert( stream.fail() && n == 0 );
    std::cout << "can't convert -1 to unsigned short" << std::endl;
    return 0;
}

我在OS X 10.5.6上的gcc(版本4.0.1 Apple Inc. build 5490)上试过这个,断言是真的;它无法将-1转换为无符号短文。

然而,在Visual Studio 2005(和2008)中,断言失败,结果n的值与编译器生成的隐式转换的结果相同 - 即“-1”为65535,“ - 2”为65534等等但是它在“-32769”处变得奇怪,转换为32767。

谁是对的,谁在这里错了? (那到底是什么-32769?)

3 个答案:

答案 0 :(得分:5)

GCC在Max Lybbert的帖子中声称的行为是基于C ++标准的表格,它将iostream行为映射到printf / scanf转换器(或者至少是那个;我的阅读)。但是,g ++的scanf行为似乎与istream行为不同:

#include <iostream>
#include <cstdio>
using namespace std;;

int main()
{
    unsigned short n = 0;
    if ( ! sscanf( "-1", "%hu", &n ) ) {
        cout << "conversion failed\n";
    }
    else {
        cout << n << endl;
    }
}

实际打印65535。

答案 1 :(得分:3)

首先,读取字符串“-1”作为负数取决于区域设置(区域设置可以通过将它们括在括号中来识别负数)。 Your default standard is the "classic" C locale

  

到目前为止, locales 的主要用途是在流I / O中隐式使用。每个 istream ostream 都有自己的区域设置。默认情况下,流的区域设置是创建流时的全局区域设置(第6页)。 ...

     

最初,全局语言环境是标准C语言环境, locale :: classic()(第11页)。

根据海湾合作委员会成员的说法,numeric overflow is allowed to fail the stream input operation(谈论溢出签名国际组织的负数):

<击>   

[T] libstdc ++的行为 - v3严格符合标准。 ...当尝试读取时,它适合签名的int i,并且失败。

感谢另一个答案,a bug was filed and this behavior changed

  

糟糕,显然我们从未正确解析无符号的负值。该   修复很简单。 ...

     

在主线中固定,也将在4.4.1中修复。

其次,尽管整数溢出通常是可预测的,但我相信它是officially undefined behavior,所以虽然我不能说为什么-32769“转换为32767,但我认为这是允许的。”/ / p>

答案 2 :(得分:1)

试试这段代码:

#include <iostream>
#include <string>
#include <sstream>
#include <cassert>

int main()
{
    std::istringstream stream( "-1" );
    std::cout << "flags: " << (unsigned long)stream.flags() << std::endl;
    return 0;
}

我在VS2005上试过这个:

flags: 513

codepad.org(我认为使用g ++)这给出了:

flags: 4098

这告诉我gcc使用不同的默认fmtflags。由于fmtflags控制了哪些转换可能会产生不同的结果。