【问题标题】:Negative numeric string (e.g. "-10") to unsigned short负数字字符串(例如“-10”)到无符号短
【发布时间】:2012-10-24 08:44:45
【问题描述】:

代码:

#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 编译器(v7、v8、v9、v10)的输出:

abc 转换失败。 10 转换为 10。 999999999999999999999 转换失败。 -10 转换为 65526。 0 转换为 0。

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

abc 转换失败。 10 转换为 10。 999999999999999999999 转换失败。 -10 转换失败。 0 转换为 0。

我预计 "-10" 无法转换为 unsigned short 但它在 VC 编译器中成功。这是一个:

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

【问题讨论】:

  • 它换行了。您的无符号短是 16 位。 stackoverflow.com/questions/2760502/…
  • 垃圾输入/垃圾输出——如果你关心字符串的有效性,那么在转换之前测试它。
  • @paddy 是定义的行为。找到了一个比第一次重复更多信息的更好的 SO 问题stackoverflow.com/questions/50605/…
  • @paddy,我的期望是什么都没有,并被告知失败。该程序测试其他垃圾输入,但未能按预期转换。
  • @paddy 在这种情况下的基本理念是使用istream 进行测试。他可能应该在 if 之前声明std::istringstream,并使用if ( stream &gt;&gt; i &gt;&gt; std::ws &amp;&amp; stream.get() == EOF ) 之类的东西,但是应该就足够了。

标签: c++


【解决方案1】:

答案取决于您使用的 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++ 所遵循的——所以无论他们做什么都是“正确的”)。

【讨论】:

    【解决方案2】:

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

    第 3 阶段:在第 2 阶段(字段)中累积的 chars 序列通过 [...] 的规则转换为数值 — 对于无符号整数值,函数 strtoull

    并且存储的数字应该是

    — 可表示的最正值,如果该字段表示的值太大而无法在 val 中表示。 ios_base::failbit 分配给err

    strtoull的操作上,C++顺从C,尝试用strtoull转换负号字段的结果有点不清楚;它声明“转换产生的值被否定(在返回类型中)”,这对于unsigned long long 将导致符号换行(到ULONGLONG_MAX - 10 + 1)。

    所以strtoull 返回的值太大而无法在unsigned short 中表示,而num_get 需要存储USHORT_MAX 并设置失败位。

    另一方面,22.4.2.1.2p3 也指出存储的数字应该是(我强调):

    ——如果字段表示的值太大而无法在val 中表示,则可以表示的最大负值或为零ios_base::failbit 分配给err

    这个子句的出现表明strtoull的规则对于带负号的字段不严格遵守;在这种解释下,num_get 需要存储0 并设置失败位。

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

    【讨论】:

    • 关于溢出的好点。 (我相当怀疑这里的 C++ 标准中仍然存在一些无意的行为。为什么在输入到 unsigned long long 时应该接受 "-10",而不是输入到其他无符号类型?)
    • 供未来访问者参考,这是开放问题LWG 1169
    猜你喜欢
    • 1970-01-01
    • 2021-12-02
    • 1970-01-01
    • 2016-11-29
    • 1970-01-01
    • 1970-01-01
    • 2011-06-18
    • 1970-01-01
    • 2015-10-21
    相关资源
    最近更新 更多