我有一个简单的测试程序(删除了错误检查):
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
int main() {
std::string line;
while(std::cin >> line) {
int value;
std::stringstream stream(line);
stream >> std::setbase(0) >> value;
std::cout << "You typed: " << value << std::endl;
}
}
哪个适用于依赖于前缀的整数解析。它会将以"0x"
或"0X"
开头的字符串解析为十六进制,将以'0'
开头的字符串解析为八进制。我在使用和见过的几个resources中对此进行了解释。然而,我无法找到的是C ++标准中的一个指示,即它可以保证有效。
strtol
中的{{1}}上的第7.20.1.4.3节(6.4.4.1是整数常量的语法)我想象提取算子在引擎盖下使用它:
如果base的值为零,则主题序列的预期形式是a的预期形式 6.4.4.1中描述的整数常量,可选地前面带有加号或减号,但是 不包括整数suf fi x。
这适用于我尝试过的几种GCC版本,但一般使用是否安全?
答案 0 :(得分:5)
setbase
在C ++ 98 [lib.std.manip] / 5中定义,稍微解释
smanip setbase(int base);
返回:未指定类型的对象 s
,以便从流中插入或提取s
的行为就像以下函数一样呼吁该流:]
ios_base& f(ios_base& str, int base)
{
str.setf(n == 8 ? ios_base::oct :
n == 10 ? ios_base::dec :
n == 16 ? ios_base::hex :
ios_base::fmtflags(0), ios_base::basefield);
return str;
}
好的,如果base
不是8,10或16,那么basefield
标志就会被清除。清除basefield
输入的效果在[lib.facet.num.get.virtuals]中定义,表55(“整数转换”)等同于sscanf("%i")
关于下一个可用的字符序列。
C ++ 98对*scanf
的定义引用C89,自然就足够了。我没有C89的PDF副本,但我确实有C99,其中7.19.6.2第12节[C标准没有C ++标准所具有的好的符号部分名称]将"%i"
定义为行为与基数参数0的strtol
相同。
所以好消息是,setbase(0)
之后的标准保证了与前缀相关的整数扫描。 坏新闻是,iostream格式化输入是根据*scanf
定义的,这意味着C99 7.19.6.2p10末尾的可怕句子适用:
如果[收到扫描结果的对象]没有合适的类型,或者转换的结果无法在对象中表示,则 行为未定义 。
(强调我的。)该句子的更清晰版本:输入溢出触发未定义的行为。如果对*scanf
的输入数字过多,则允许C(++)运行时使程序崩溃!这是(原因之一)为什么我和其他人一直说*scanf
永远不应该使用,现在我必须开始说istream >> int
。 : - (
对C语言的建议更容易在C ++中应用:使用std::getline
读取整行并手动解析它们。使用strtol
系列函数将数字输入转换为机器编号。 (这些函数在溢出时具有可预测的行为。)
答案 1 :(得分:3)
§22.4.2.1.2/ 3,表85:
为了转换为整数类型,该函数确定如表85所示的积分转换说明符。该表是有序的。也就是说,条件为真的第一行适用。
Table 85 — Integer conversions
State stdio equivalent
basefield == oct %o
basefield == hex %X
basefield == 0 %i
signed integral type %d
unsigned integral type %u
%i
和公司的scanf
转换格式会进行前缀相关转换。
答案 2 :(得分:2)
让我们从§27.6.3,“标准操纵者”,¶5,“smanip setbase(int base)
”开始:
返回:未指定类型的对象
s
,如果in
是({1}}的实例,那么 表达式basic_istream
的行为就像调用in>>s
一样。其中f(s)
可以定义为:f
我们继续使用§27.4.2.2ios_base& f(ios_base& str, int base)
{
// set basefield
str.setf(base == 8 ? ios_base::oct :
base == 10 ? ios_base::dec :
base == 16 ? ios_base::hex :
ios_base::fmtflags(0), ios_base::basefield);
return str;
}
状态函数,¶6ios_base fmtflags
效果:清除
fmtflags setf(fmtflags fmtfl, fmtflags mask);
中的mask
,在flags()
中设置fmtfl & mask
。
那么,在flags()
中设置0&basefield
会产生什么影响?
考虑§27.6.1.2.2算术提取器,其中描述了flags()
:
这些提取器取决于语言环境的num_get&lt;&gt; (22.2.2.1)反对 执行解析输入流数据。
§22.2.2.1,¶4,表55描述了在这种情况下选择的转换说明符:
operator>>(int& val);
最后,¶11说:
一系列字符...被转换(根据规则)
basefield == 0, `%i`
)到val类型的值。
因此,C ++ Standard,2003表示scanf
相当于std::cin >> setbase(0) >> i
。
对于 的含义,您需要参考C标准。