逆波兰表达式(RPN)求值可用std::stack安全实现:遍历token,数字(含合法负数如"-123")转整数并入栈,运算符则先检查栈大小≥2,再弹出右操作数b和左操作数a,执行运算后压栈;需try-catch处理stoi异常、除零检查及空栈防护。

直接用 std::stack 实现逆波兰表达式(RPN)求值是可行的,但关键不在“能不能”,而在“怎么处理操作数、运算符、负数和非法输入”——稍不注意就会遇到 std::stoi 抛异常、栈空时弹出、除零未检查等问题。
如何安全地将 token 转为整数(尤其处理负号)
RPN 输入中像 "-123" 是合法操作数,但不能靠 isdigit() 简单判断首字符。C++ 的 std::stoi 本身支持带符号字符串,但会抛 std::invalid_argument 或 std::out_of_range。
- 先用
token[0] == '-' && token.size() > 1 && std::all_of(token.begin()+1, token.end(), ::isdigit)快速筛掉明显非法的负号串(如"-"或"--5") - 真正转换时务必套
try-catch,捕获std::invalid_argument和std::out_of_range,并返回错误标识(如抛自定义异常或用std::optional<int>) - 不要用
atoi(token.c_str()):对非法输入返回 0,无法区分"0"和"abc"
为什么必须检查栈大小再 pop 两个操作数
运算符(如 "+")需要从栈顶取两个操作数,顺序是:先 pop() 得右操作数,再 pop() 得左操作数。若栈中元素不足 2 个,std::stack::top() 和 pop() 行为未定义(实际常 crash 或读脏内存)。
- 每次遇到运算符前,先用
if (stack.size() < 2)做防御性检查 - 推荐统一写法:
int b = stack.top(); stack.pop(); int a = stack.top(); stack.pop();—— 注意变量名a/b对应左/右,避免减法、除法反序 - 除法要额外检查
b == 0,否则运行时 SIGFPE(浮点异常,整数除零也触发)
std::stack 的性能与替代方案
std::stack 默认基于 std::deque,对 RPN 这种“只在尾部 push/pop”的场景,其实 std::vector 更高效(缓存友好、分配更少)。
立即学习“C++免费学习笔记(深入)”;
- 可显式指定底层容器:
std::stack<int, std::vector<int>> stk; - 不用
std::list:指针跳转多,缓存不友好;也不用std::array(大小固定,RPN 长度未知) - 如果追求极致性能且输入长度可控,可预分配
std::vector并用手动索引模拟栈(top_idx),但通常没必要
最易被忽略的是 token 分割逻辑:RPN 字符串通常以空格分隔,但可能有连续空格、首尾空格。用 std::istringstream 或手写跳过空白的循环比 std::split(C++20 才有)更稳妥。另外,所有运算符只支持二元运算,不支持一元负号(如 "5 -3 -" 中的 "-3" 是操作数,不是一元减),这点必须和需求对齐。


















