《The C++ Programming Language第4版》桌面计算器输入pi输出错误如何修复?
C++桌面计算器预定义常量读取错误问题修复方案
我在复制Bjarne Stroustrup所著《The C++ Programming Language Fourth Edition》中的桌面计算器代码运行时,发现输入代码中已预定义的常量pi时输出结果错误,耗时1天定位到问题根源,现将问题场景、完整代码及修复方案分享如下:
完整可运行代码
#include <iostream> #include <map> #include <string> #include <sstream> #include <cctype> enum class Kind:char{ name,number,end, plus='+',minus='-',mul='*',div='/',print=';',assign='=',lp='(',rp=')' }; struct Token{ Kind kind; std::string string_value; double number_value; }; int no_of_errors; double error(const std::string& s) { no_of_errors++; std::cerr << "error: " << s << '\n'; return 1; } std::map<std::string,double> table; double expr(bool get); double term(bool get); double prim(bool get); class Token_stream{ public: Token_stream(std::istream& s):ip{&s},owns{false}{} Token_stream(std::istream* p):ip{p},owns{true}{} ~Token_stream(){close();} Token get(); Token& current(){return ct;} void set_input(std::istream& s){close();ip=&s;owns=false;} void set_input(std::istream* p){close();ip=p;owns=true;} private: void close(){if(owns) delete ip;} std::istream* ip; bool owns; Token ct{Kind::end}; }; Token_stream ts{std::cin}; Token Token_stream::get() { char ch; do{ if(!ip->get(ch)){ return ct={Kind::end}; } }while(ch!='\n' && std::isspace(ch)); switch(ch){ case ';': case '\n': return ct={Kind::print}; case '*': case '/': case '+': case '-': case '(': case ')': case '=': return ct={static_cast<Kind>(ch)}; case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9': case '.':{ ip->putback(ch); *ip >> ct.number_value; ct.kind=Kind::number; return ct; } default: if(std::isalpha(ch)){ ct.string_value = ch; while(ip->get(ch) && std::isalnum(ch)){ ct.string_value +=ch; } ip->putback(ch); return ct={Kind::name,ct.string_value}; } error("bad token"); return ct={Kind::print}; } } double expr(bool get) { double left=term(get); for(;;){ switch(ts.current().kind){ case Kind::plus:left += term(true);break; case Kind::minus:left -= term(true);break; default:return left; } } } double term(bool get) { double left = prim(get); for(;;){ switch(ts.current().kind){ case Kind::mul:left *= prim(true);break; case Kind::div: if(auto d = prim(true)){left /= d;break;} return error("divide by 0"); default:return left; } } } double prim(bool get) { if(get) ts.get(); switch(ts.current().kind){ case Kind::number:{ double v=ts.current().number_value; ts.get(); return v; } case Kind::name:{ double& v=table[ts.current().string_value]; if(ts.get().kind == Kind::assign) v=expr(true); return v; } case Kind::lp:{ auto e = expr(true); if(ts.current().kind!=Kind::rp) return error("')' expected"); ts.get(); return e; } default:return error("primary expected"); } } void calculate() { for(;;){ ts.get(); if(ts.current().kind == Kind::end)break; if(ts.current().kind == Kind::print)continue; std::cout << expr(false) << '\n'; } } int main(int arg,char* argv[]) { switch(arg){ case 1:break; case 2:ts.set_input(new std::istringstream{argv[1]});break; default:error("too many arguments");return 1; } table["pi"] = 3.1415926535897932385; table["e"] = 2.7182818284590452354; calculate(); return no_of_errors; }
问题修复方案
问题出在Token_stream::get()成员函数的name类型Token构造逻辑中:
- 错误代码:
// 位于Token_stream::get()成员函数内 return ct={Kind::name};
错误原因:构造name类型Token时未传入已经解析完成的字符串值,导致后续从预定义常量表中查找时匹配不到对应的key,返回默认值0而非预设的常量值
- 修复后代码:
// 位于Token_stream::get()成员函数内 return ct={Kind::name, ct.string_value};
内容的提问来源于stack exchange,提问作者ouyang_csu
相关产品推荐
相关产品推荐

