在调用 va_end 之前可以 longjmp 吗?

Is it okay to longjmp before calling va_end?

在此问答中,您应始终调用 va_end():

What exactly is va_end for? Is it always necessary to call it?

但是如果在到达 va_end 之前有一段代码 longjmp 怎么办? va_end 是否承诺会没事的?或者从概念上讲,它 (例如)va_start() 中进行内存分配会泄漏,而不是仅使用堆栈技巧?

C99 rationale 明确指出 va_start 可能会分配最终由 va_end 释放的内存,这正是您在问题中猜到的:

7.15.1.2 The va_copy macro

[...]

30 A much simpler approach is to copy the va_list object used to represent processing of the arguments. However, there is no safe way to do this in C89 because the object may include pointers to memory allocated by the va_start macro and destroyed by the va_end macro.
The new va_copy macro provides this safe mechanism.

[...]

所以是的,您需要在 longjmp 之前调用 va_end。至少你会在这样的实现上发生内存泄漏。


据说 Pyramid OSx 有一个内存分配由 va_start 执行的实现。函数参数在寄存器中传递。即使对于可变参数函数也是如此。它可能早于 ANSI C 发明的函数原型,这意味着调用者不知道它是否在处理可变参数函数。 va_start 分配的内存,大概是为了以 va_arg 可以轻松访问的方式存储函数参数值。 va_end 释放了分配的内存。

va_startva_end 的实现实际上需要在语法上匹配 va_startva_end,因为它使用了不平衡的大括号,所以 ANSI C 已经不允许实施,但同样的原则可以在匹配大括号时起作用。

关于这个实现我能找到的具体信息很少,它只是 80 年代末 90 年代初 Usenet 上的一些零碎信息。我所发现的一点点可能是不完整的,甚至是完全错误的。非常欢迎提供更多详细信息,尤其是那些自己使用过此实现的人。

如果您正在使用存储在全局变量中的 jmp_buff(通常的模式),复制它并使用 setjmp 应该是安全的,因此 longjmp 将转到您的代码而不是外部调用者;在 longjmp 的情况下,您的代码可以使用缓冲区的存储副本调用 va_endlongjmp;如果您的代码正常退出,则需要在返回之前恢复全局缓冲区。