Ach*_*raf 1 c stack callstack goto setjmp
我创建了一个使用 RDP(递归下降解析器)的计算器来解析和评估数学表达式“例如:5 cos(30) -5 (3+5)”。问题是我还尝试包含像“config”这样的命令,它可以让用户配置角度单位(Gra、Rad、Deg)。而这里我发现了代码设计的问题。
调用堆栈的简化示例:
int main()
{
parseexpr()
{
parseterm()
{
parsefactor()
{
parsecommand() or parsefct() or parsenumber();
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在当用户输入“config”命令时。我想让他配置计算器(配置位于 .txt 文件内),然后跳回“main()”(他会被要求再次输入表达式),因为通过调用返回会出现问题堆栈,因为我需要一个返回值,而“parsecommand()”无法返回。
我的问题是如何清理调用堆栈,以便我可以通过“goto”语句直接返回到 main ?
我的问题是如何清理调用堆栈,以便我可以通过“goto”语句直接返回到 main ?
不要那样做。这实际上就是所谓的“XY问题”。
正确的做法是这样的:
将所有这些函数放在它们自己的模块中,我们称之为parse.h / parse.c。
提出parse.h一个由该模块中所有适用函数使用的结果/错误类型。就像是:
typedef enum
{
/* whatever results that may make sense: */
PARSE_OK,
PARSE_BAD_FORMAT,
PARSE_DIV_BY_ZERO,
...
} parse_result_t;
Run Code Online (Sandbox Code Playgroud)
适用的函数定义为:
parse_result_t parse_expr (/* parameters */);
Run Code Online (Sandbox Code Playgroud)
需要返回的任何值都作为指针参数传递。
在嵌套的多个函数调用中,每个函数都会检查结果,如果不正确,则向调用者返回错误代码。通常,在出现错误时不修改值参数被认为是良好的做法。
错误的、非常糟糕的做法替代方案是利用setjmp/ longjmp,它们是实际存储和恢复调用堆栈和其他类似变量的低级函数。因此理论上可以在标准 C 中实现,但这些函数是不好的做法,因为:
jmp_buf缺少某些全局变量,然后我应该以某种方式在 main() 中实现它。或者更糟糕的是:为了链接parse.c我还必须链接main.c. 这种紧密耦合的依赖关系既不可移植也不可维护,几乎在所有可以想象的方面都是糟糕的。setjmp/longjmp附带一长串危险和潜在的未定义行为。| 归档时间: |
|
| 查看次数: |
148 次 |
| 最近记录: |