[英]Safe usage of `setjmp` and `longjmp`
我知道人们总是说不要使用longjmp
,它是邪恶的,它是危险的。
但我认为它对退出深度递归/嵌套函数调用很有用。
单个longjmp
比很多重复检查和返回更快,如if(returnVal != SUCCESS) return returnVal;
?
至于安全性,只要动态内存和其他资源正确发布,就不会出现问题,对吧?
到目前为止似乎使用longjmp
并不困难,它甚至使我的代码更加严格。 我很想使用它。
(恕我直言,在很多情况下,首先在深度递归中没有分配动态内存/资源。深度函数调用似乎更常见于数据解析/操作/验证。动态分配通常发生在更高级别,在调用函数之前setjmp
出现。)
setjmp
和longjmp
可以看作是一个穷人的异常机制。 顺便说一句, Ocaml异常与setjmp
一样快,但语义更清晰。
当然, longjmp
比在中间函数中重复返回错误代码要快得多,因为它会弹出一个可能很重要的调用堆栈部分。
(我隐含地专注于Linux)
只要没有在它们之间分配资源,它们就是有效且有用的,包括:
malloc
) fopen
-ing FILE*
处理 主要问题是不泄漏资源的属性是全局整体程序属性 (或者至少是可能在setjmp
和longjmp
之间调用的所有函数的全局属性 ),因此它禁止模块化软件开发 :任何其他同事必须改进一些代码setjmp
和longjmp
之间的任何函数都必须知道该限制并遵循该规则。
因此,如果您非常清楚地使用 setjmp
文档。
顺便说一句,如果你只关心malloc
, 系统地使用Boehm的保守垃圾收集器会有很大帮助; 你将在任何地方使用GC_malloc
而不是malloc
,你不会关心free
,实际上GC_malloc
足够了; 那么你可以毫无恐惧地使用setjmp
(因为你可以在setjmp
和longjmp
之间调用GC_malloc
)。
(请注意,垃圾收集器周围的概念和术语与异常处理和setjmp
密切相关,但很多人对它们了解不多。阅读垃圾收集手册应该是值得的)
阅读有关RAII的内容并了解C ++ 11异常(以及它们与析构函数的关系)。 了解延续和CPS 。
读取setjmp(3) , longjmp(3) (以及关于sigsetjmp
, siglongjmp
和setcontext(3) )并注意编译器必须知道setjmp
您应该注意,在某些上下文中调用setjmp并不保证是安全的(例如,您无法移植存储setjmp的返回值)。
此外,如果要在调用setjmp之后访问局部变量,在同一函数中可能已更改,则应将变量标记为volatile。
使用setjmp和longjmp也很有用,因为如果递归导致堆栈溢出,您可以使用信号处理程序中的longjmp进行恢复(不要忘记设置备用堆栈)并返回错误。 如果你想这样做,你应该考虑使用sigsetjmp和siglongjmp来保留信号处理。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.