Col*_*lin 9 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;
}
}
Run Code Online (Sandbox Code Playgroud)
哪个适用于依赖于前缀的整数解析.它会解析字符串开头"0x"或"0X"十六进制和字符串开始'0'为八进制.我在使用和看过的几个资源中对此进行了解释.然而,我无法找到的是C++标准中的一个指示,即它可以保证有效.
C标准strtol中的第7.20.1.4.3节说(6.4.4.1是整数常量的语法)我想抽象运算符在引擎盖下使用它:
如果base的值为零,则主题序列的预期形式是如6.4.4.1中所述的整数常量的形式,可选地前面加上或减号,但不包括整数suf fi x.
这适用于我尝试过的几个GCC版本,但一般使用它是否安全?
setbase 在C++ 98 [lib.std.manip]/5中定义,略有释义
smanip setbase(int base);
Run Code Online (Sandbox Code Playgroud)
返回: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;
}
Run Code Online (Sandbox Code Playgroud)
好的,所以,如果base不是8,10或16,则basefield清除标志.清除输入的效果basefield在[lib.facet.num.get.virtuals],表55("整数转换")中定义为与sscanf("%i")下一个可用字符序列等效.
C++ 98引用C89的定义*scanf,自然就够了.我没有C89的PDF副本,但我确实有C99,其中第7.19.6.2节第12段[C标准没有C++标准所具有的好的符号部分名称]定义"%i"的行为strtol与基础参数0.
所以好消息是,依赖于前缀的整数扫描是由标准保证的setbase(0).在坏消息是,iostream的格式化输入中的定义的*scanf,这意味着在C99 7.19.6.2p10结束可怕的句子适用:
如果[接收扫描结果的对象]没有合适的类型,或者无法在对象中表示转换结果,则行为未定义.
(强调我的.)该句子更清晰:输入溢出触发未定义的行为.如果输入有太多数字,则允许C(++)运行时崩溃程序*scanf!这是(其中一个原因)为什么我和其他人一直说不*scanf应该使用,现在我必须开始说它istream >> int.:-(
对C语言的建议更容易在C++中应用:阅读整行std::getline并手动解析它们.使用strtol函数族将数字输入转换为机器编号.(这些函数在溢出时具有可预测的行为.)