哪里定义了前缀相关的整数解析?

时间:2012-11-02 13:48:30

标签: c++ language-lawyer

我有一个简单的测试程序(删除了错误检查):

#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版本,但一般使用是否安全?

3 个答案:

答案 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标准。