4

考虑这个程序:

#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 到底是怎么回事??)

4

3 回答 3

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。

于 2009-04-24T23:42:59.487 回答
3

首先,将字符串“-1”读取为负数取决于语言环境(语言环境可以通过将负数括在括号中来识别负数)。 您的默认标准是“经典”C 语言环境

到目前为止,语言环境的主要用途是在流 I/O 中隐式使用。每个istreamostream都有自己的locale。默认情况下,流的语言环境是创建流时的全局语言环境(第 6 页)。...

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

根据 GCC 家伙的说法,允许数字溢出使流输入操作失败(谈论溢出有符号 int 的负数):

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

感谢另一个答案,提交了一个错误并且此行为发生了变化

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

已在主线中修复,也将在 4.4.1 中修复。

其次,虽然整数溢出通常是可以预测的,但我相信这是官方未定义的行为,所以虽然我不能说为什么 -32769" 转换为 32767,但我认为这是允许的。

于 2009-04-24T22:32:22.463 回答
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 使用不同的 default fmtflags。由于fmtflags控制了可能的转换,您将获得不同的结果。

于 2009-04-24T18:27:58.947 回答