代码:
#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编译器。这是一个:
答案 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
并设置失败位。
在任何一种情况下,转换都需要失败。