11

在本问答中,您应该始终致电va_end()

va_end 到底是做什么用的?是否总是需要调用它?

但是如果在你到达 va_end 之前有一段代码 longjmp 呢?va_end 是否有任何保证会好起来的承诺?或者从概念上讲,它(例如)可能会进行内存分配va_start(),而不是仅使用堆栈技巧?

4

2 回答 2

8

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

7.15.1.2va_copy

[...]

30 一个更简单的方法是复制va_list用于表示参数处理的对象。但是,在 C89 中没有安全的方法来执行此操作,因为对象可能包含指向由宏分配并被va_start宏销毁的内存的指针va_end
va_copy宏提供了这种安全机制。

[...]

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


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

它的实现实际上需要匹配va_start和语法,因为它使用了不平衡的大括号,所以 ANSI C 已经不允许这种实现,但是在匹配大括号时可以使相同的原理起作用。va_endva_startva_end

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

于 2015-08-27T21:25:59.723 回答
2

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

于 2015-08-27T22:24:43.433 回答