setjmp和longjmp的组合是“超强的goto“。使用时要格外小心。然而,正如其他人所解释的,当您想要快速地get me back to the beginning时,longjmp对于摆脱讨厌的错误情况非常有用,而不是必须为18层函数涓涓细流地返回错误消息。 然而,就像goto一样,更糟糕的是,你必须非常小心地使用它。longjmp只会让你回到代码的开头。它不会影响在setjmp和回到setjmp开始的地方之间可能已经改变的所有其他状态。所以分配、锁、半初始化的数据结构等仍然是分配的。当你回到调用setjmp的地方时,它被锁定和半初始化。这意味着,你必须真正关心你这样做的地方,调用longjmp而不引起更多的问题是真的可以的。当然,如果你做的下一件事是“重新启动”[在存储了关于错误的消息后,也许] -例如,在嵌入式系统中,您发现硬件处于不良状态,则可以。 我也见过setjmp/longjmp用来提供非常基本的线程机制,但这是非常特殊的情况--而且绝对不是“标准”线程的工作方式。 编辑:当然可以添加代码来“处理清理”,就像C++在编译后的代码中存储异常点,然后知道是什么引起了异常,什么需要清理一样。这将涉及到某种函数指针表,并存储“如果我们从下面这里跳出来,调用这个函数,使用这个参数”。类似于这样:
8条答案
按热度按时间xytpbqjk1#
假设嵌套在许多其他函数中的函数内部存在一个错误,而错误处理只有在顶级函数中才有意义。
如果中间的所有函数都必须正常返回,并计算返回值或全局错误变量,以确定进一步的处理没有意义,甚至是不好的,这将是非常乏味和笨拙的。
这种情况下setjmp/longjmp是有意义的,这些情况类似于其他语言(C++,Java)中异常的情况。
除了错误处理之外,我还可以想到另一种情况,在C中需要setjmp/longjmp:
当您需要实现coroutines时就是这种情况。
这里有一个小的演示示例,我希望它能满足Sivaprasad Palas对一些示例代码的请求,并回答TheBlastOne的问题setjmp/longjmp如何支持corroutines的实现(就我所见,它并不基于任何非标准或新的行为)。
它可能实际上 * 是 * 未定义的行为来执行
longjmp
* down * 调用堆栈(参见MikeMB的注解;尽管我还没有机会证实这一点)。下图显示了执行流程:
在使用setjmp/longjmp时,请注意它们对局部变量的有效性有影响,但通常不会考虑。
参见我的question about this topic。
uoifb46i2#
其原理是,您可以使用它们进行错误处理,这样您就可以跳出深度嵌套的调用链,而无需处理链中每个函数的错误处理。
就像所有聪明的理论一样,当遇到现实时,这个理论就福尔斯了。你的中间函数会分配内存,抓取锁,打开文件,做各种各样需要清理的事情。所以在实践中,
setjmp
/longjmp
通常是个坏主意,除非在非常有限的情况下,你可以完全控制你的环境(一些嵌入式平台)。根据我的经验,在大多数情况下,当您认为使用
setjmp
/longjmp
可以工作时,您的程序足够清晰和简单,调用链中的每个中间函数调用都可以进行错误处理,或者它太混乱,无法修复,当您遇到错误时,您应该使用exit
。eit6fx6z3#
我用C语言编写了一个Java-like exception handling mechanism,使用了
setjmp()
、longjmp()
和系统函数。它捕获自定义异常,但也捕获SIGSEGV
之类的信号。它具有异常处理块的无限嵌套的特点,这种嵌套在函数调用之间工作,并支持两种最常见的线程实现。它允许您定义异常类的树层次结构,这种异常类具有链接时继承的特点。并且catch
语句遍历该树以查看它是否需要捕获或传递。下面是使用此函数的代码示例:
下面是包含大量逻辑的include文件的一部分:
还有一个C模块,包含信号处理和一些簿记的逻辑。
我可以告诉你,它的实现是极其棘手的,我几乎放弃了。我真的努力使它尽可能接近Java;我发现仅仅用C就能走这么远真是令人惊讶。
有兴趣的话就给予我一声。
n3h0vuf24#
setjmp
和longjmp
的组合是“超强的goto
“。使用时要格外小心。然而,正如其他人所解释的,当您想要快速地get me back to the beginning
时,longjmp
对于摆脱讨厌的错误情况非常有用,而不是必须为18层函数涓涓细流地返回错误消息。然而,就像
goto
一样,更糟糕的是,你必须非常小心地使用它。longjmp
只会让你回到代码的开头。它不会影响在setjmp
和回到setjmp
开始的地方之间可能已经改变的所有其他状态。所以分配、锁、半初始化的数据结构等仍然是分配的。当你回到调用setjmp
的地方时,它被锁定和半初始化。这意味着,你必须真正关心你这样做的地方,调用longjmp
而不引起更多的问题是真的可以的。当然,如果你做的下一件事是“重新启动”[在存储了关于错误的消息后,也许] -例如,在嵌入式系统中,您发现硬件处于不良状态,则可以。我也见过
setjmp
/longjmp
用来提供非常基本的线程机制,但这是非常特殊的情况--而且绝对不是“标准”线程的工作方式。编辑:当然可以添加代码来“处理清理”,就像C++在编译后的代码中存储异常点,然后知道是什么引起了异常,什么需要清理一样。这将涉及到某种函数指针表,并存储“如果我们从下面这里跳出来,调用这个函数,使用这个参数”。类似于这样:
有了这个系统,你可以做“像C++一样完整的异常处理",但它相当混乱,而且依赖于代码的良好编写。
ndh0cuux5#
setjmp
和longjmp
在单元测试中非常有用。假设我们要测试以下模块:
通常情况下,如果要测试的函数调用另一个函数,您可以声明一个存根函数供其调用,该函数将模拟实际函数测试某些流的操作。然而,在这种情况下,函数调用
exit
,该函数不会返回。存根需要以某种方式模拟此行为。setjmp
和longjmp
可以为您完成此操作。要测试此功能,我们可以创建以下测试程序:
在本例中,在输入要测试的函数之前使用
setjmp
,然后在存根化的exit
中调用longjmp
直接返回测试用例。还要注意,重新定义的
exit
有一个特殊的变量,它会检查你是否真的想退出程序,并调用_exit
来退出,如果你不这样做,你的测试程序可能不会干净地退出。kqqjbcuj6#
既然您提到了嵌入式,我认为值得注意一个非使用情形:例如MISRA(MISRA-C:2004:规则20.7)和JFS(AV规则20):“不应使用setjmp宏和longjmp函数。”
tyu7yeag7#
毫无疑问,setjmp/longjmp最关键的用途是它充当“非本地后藤跳转”。(在极少数情况下,您需要在for和while循环上使用后藤)是在同一作用域中使用最安全的。(或跨自动分配),你将很可能损坏你的程序的栈。setjmp/longjmp通过保存栈信息在你想要跳转到的位置来避免这种情况。然后,当你跳转时,它加载堆栈信息。如果没有这个特性,C程序员很可能不得不求助于汇编编程来解决只有setjmp/longjmp才能解决的问题。感谢上帝,它存在。C库中的所有内容都是极其重要的。当你需要它时,你会知道。
js81xvg68#
除了错误处理之外,你可以做的另一件事是在C语言中以一种聪明的方式实现尾部递归计算。
这实际上是如何在C中实现延续,而不需要以延续传递的方式转换输入代码。