负数字字符串(例如“-10”)到无符号短

时间:2012-10-24 08:44:45

标签: c++

代码:

#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
using std::cerr;
using std::cout;
using std::stringstream;
using std::string;
using std::for_each;

void convert(const string& a_value)
{
    unsigned short i;
    if (stringstream(a_value) >> i)
        cout << a_value << " converted to " << i << ".\n";
    else
        cerr << a_value << " failed to convert.\n";
}

int main()
{
    string inputs[] = { "abc", "10", "999999999999999999999", "-10", "0" };
    for_each(inputs, inputs + (sizeof(inputs)/sizeof(inputs[0])), convert);
    return 0;
}

Visual Studio Compiler的输出(v7,v8,v9,v10):

abc failed to convert.
10 converted to 10.
999999999999999999999 failed to convert.
-10 converted to 65526.
0 converted to 0.

g ++(v4.1.2,v4.3.4)的输出:

abc failed to convert.
10 converted to 10.
999999999999999999999 failed to convert.
-10 failed to convert.
0 converted to 0.

我希望"-10"无法转换为unsigned short,但它会成功使用VC编译器。这是一个:

  • VC编译器中的错误?
  • GNU编译器中的错误,我的期望不正确?
  • 实现定义的行为?

2 个答案:

答案 0 :(得分:5)

答案取决于您使用的C ++版本。 C ++ 03和 之前要求输入符合sscanf所做的(使用此处) "%hi"输入说明符),sscanf读取整数值 a(签名)短,没有溢出检测;那么结果就是 已分配(带隐式转化)到您的unsigned short。 C ++ 11 需要相当于调用strtoull,这不允许 -符号,并在溢出时需要错误(未定义) sscanf中的行为,因而是C ++ 03)。

在实践中,C ++ 03的所有合理实现都会检查 过度,在这种情况下“未定义的行为”与此相对应 这是现在需要的。另一方面,他们必需 接受减号,现在(逻辑上)禁止。

编辑(更正): 在重新阅读strtoull的要求时,我发现它确实需要接受减号。因此看起来很愚蠢,标准确实需要输入无符号整数类型来接受减号。 (另请注意,strtoull的行为取决于全局C语言环境,这可能会接受其他可能性。)

编辑(进一步澄清): 正如ectamur指出的那样,这应该是一个错误(在C ++ 11中),因为(unsigned long long)( -10 )太大而无法在unsigned short中表示。另一方面,它仍然是前C ++ 03中的未定义行为(这可能是VC ++所遵循的 - 所以无论他们做什么都是“正确的”)。

答案 1 :(得分:3)

g ++是正确的。无符号整数类型的算术提取器在27.7.2.2.2p1中定义为取决于num_get<>; 22.4.2.1.2p3声明:

  

阶段3:阶段2(字段)中累积的char的序列通过[...]的规则转换为数值 - 对于无符号整数值,函数{{1 }}

并且存储的数字应为

  

- 最积极的可表示值,如果该字段表示的值太大而无法在strtoull中表示。 val被分配到ios_base::failbit

关于err的操作,C ++遵循C,这对于尝试使用strtoull转换负号字段的结果有点不清楚;它声明“转换产生的值被否定(在返回类型中)”,strtoull将导致签名换行(到unsigned long long)。

因此ULONGLONG_MAX - 10 + 1返回的值太大而无法在strtoull中表示,并且unsigned short需要存储num_get并设置失败位。

另一方面,22.4.2.1.2p3还指出存储的数字应该是(我的重点):

  

- 无效的可表示值或无符号整数类型的零,如果该字段表示的值太大而无法在USHORT_MAX中表示。 val被分配到ios_base::failbit

该条款的存在表明,对于具有负号的字段,不严格遵守err的规则;根据这种解释,strtoull需要存储num_get并设置失败位。

在任何一种情况下,转换都需要失败。