双栈法是最稳妥的中缀表达式求值方法,因其能清晰处理括号嵌套、一元负号(如-5、-(3+2))、运算符优先级与结合性,且覆盖连续负号、前导负号等全部合法情况。

为什么双栈法是中缀求值最稳妥的选择
手写递归下降或词法分析器容易在括号嵌套、负号、一元运算符(如 -5 或 -(3+2))上出错;而标准库没有现成的 std::evaluate_expression。双栈法(操作数栈 + 运算符栈)用确定性规则处理优先级和结合性,逻辑清晰、边界可控,适合工程中快速落地。
关键判断依据是:只要输入是合法中缀字符串(含整数、+-*/()),双栈法就能覆盖全部情况,包括:
• 连续负号:-(-3)
• 前导负号:-5+2
• 除零不检查(需额外判断)
• 整数除法向零截断(C++ 默认行为,无需特殊处理)
如何正确识别一元减号而非二元减号
一元减号本质是“符号”,不是运算符,不能直接压入运算符栈。必须在扫描时结合上下文判断:
• 开头位置(如 -3+2 的第一个 -)
• 左括号后(如 (-3) 中的 -)
• 运算符后(如 5*-3 中的 -)
实操建议:
• 遇到 - 时,检查前一个 token 类型(非数字、非右括号)→ 视为一元负号
• 立即读取后续数字或左括号,构造成一个带符号的数值压入操作数栈
• 不要把它当作 operator- 压栈,否则后续计算会错乱(比如把 -3 拆成 0 - 3 引入冗余操作)
• 若用字符流扫描,可用一个 bool expect_operand = true 标志位控制:遇到 + 或 - 且 expect_operand 为真 → 一元符号
立即学习“C++免费学习笔记(深入)”;
运算符优先级比较与栈顶运算执行时机
C++ 中没有内置优先级表,必须手动定义。常见错误是把 ( 的优先级设得过高,导致无法正常弹出;或忽略右结合性(如 ^,但本题不含)。
推荐优先级映射(数字越大优先级越高):
• '(' → 0(仅用于占位,不参与比较)
• '+', '-' → 1
• '*', '/' → 2
• ')' → -1(特殊:触发弹栈直到遇到 '(')
执行计算的条件(伪代码逻辑):
• 当前字符是运算符(非括号)且栈非空
• 栈顶运算符优先级 ≥ 当前运算符优先级(注意:对 +- 是左结合,所以相等也要弹)
• 遇到 ')' 时,持续弹出直到 '(',且不压入 ')'
示例:"3+2*4"
扫描到 * 时,栈顶是 +(优先级 1 扫描到末尾或下一个低优先级符(如 + 或 ')')时,才弹出 * 并计算 2*4。
字符解析细节:跳过空格、处理多位数、避免 stoi 异常
中缀字符串通常含空格,但 C++ std::stoi 遇到非数字字符会截断并返回部分结果,不抛异常——这会导致 "12a+3" 被误读为 12,掩盖错误。
安全做法:
• 用 std::isdigit 手动收集连续数字字符,再用 std::stoll 或自定义转换(避免溢出)
• 遇到非数字非运算符非括号字符(如字母、.)立即报错:"invalid character: 'a'"
• 空格直接跳过,不参与任何状态判断
• 对于负数,先捕获 -,再确保后面紧跟数字或 (,否则报错:"-+" 或 "-)" 是非法的
容易被忽略的一点:右括号后可能紧接乘法,如 "(2+3)4" 应解释为 (2+3)*4。这需要在解析完右括号后,检查下一个字符是否为数字或 '(',若是,则隐式插入 '*' —— 这个隐式乘号必须参与优先级判断,不能漏掉。











