C 在打电话给va_end之前可以先打个电话吗?

C 在打电话给va_end之前可以先打个电话吗?,c,language-lawyer,c89,setjmp,variadic-functions,C,Language Lawyer,C89,Setjmp,Variadic Functions,在本问答中,您应该始终调用va_end(): 但是如果在你到达va_端之前有一段代码longjmp呢?va_end是否承诺一切都会好起来?或者从概念上讲,它(例如)可能会在va_start()中进行内存分配,这可能会泄漏,而不仅仅是使用堆栈技巧?明确指出,va_start可能会分配最后由va_end释放的内存,这正是您在问题中猜到的: 7.15.1.2va_副本宏 [……] 30一种更简单的方法是复制用于表示参数处理的va_list对象。然而,没有安全的方法 在C89中执行此操作,因为对象可能

在本问答中,您应该始终调用
va_end()

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

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

7.15.1.2
va_副本

[……]

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

[……]

因此,是的,您需要在
longjmp
之前调用
va_end
。至少在这种实现上会出现内存泄漏


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

它的
va_start
va_end
的实现实际上需要语法上的匹配
va_start
va_end
,因为它使用了不平衡的大括号,所以ANSI C已经不允许这种实现,但在有匹配大括号的情况下,同样的原则也可以发挥作用


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

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

我想您需要在调用
longjmp
之前调用
va_end
,同样的方式,您应该
free()
任何被跳过的错误定位不是问题的重点吗?@WeatherVane Yes。问题是,如果处理每个参数项所做的工作可以使用jmp,那么该怎么办。如果是这样,那么在进行调用之前,您必须将变量args放在另一个地方…但是放在什么地方呢?可能是有大小限制的有界堆栈数组。如果va_args拥有的东西已经可以用作那个地方,那就太好了……在实际的实现中,通常是这样。但该标准不太可能保证这一点。@supercat我在嵌入式系统中也使用了一种残忍的方法作为最后手段,当所有其他方法都失败时,但节目必须继续,它会使您提到的部分重新启动,没有什么悬而未决。@supercat在有或没有多任务的嵌入式系统中,我发现最好的方法是中间“心跳”由计时器驱动的层,它将为来自中断的信息提供服务,并按原样将其呈现给“主循环”,尽管这是一个简化的语句。处理错误的最佳方法是将中断中的错误作为状态值传递,或将子例程中的错误作为返回值传递。我把跳远看作是短路的软件等价物。如果所有其他操作都失败,则相当于重置按钮。如果你设计长跳转到基于堆栈的系统中是因为你可以,那你就太丢脸了。我认为也值得引用
va_end
的基本原理,它包含了许多实现中的
,这是一个什么都不做的操作;但是那些需要它的实现可能非常需要它。
<代码>非常非常模糊,“那些需要它的实现可能非常需要它”。。。嘿,真有趣。:-)@cremno我试图通过找到一个具体的实现示例来说明这一点,其中,
va_end
不是一个no op,但我怀疑我是否会找到它。只需添加当前的标准:和。但是,它只引用“return”,而不是longjmp,也不再涉及分配的内存。我不太清楚:
stdarg.h
不需要
stdlib.h
或任何其他内存分配函数。那么它是否仅指堆栈分配/自动内存?如果是这样,longjmp会不会正确处理这个问题?@Olaf
stdarg.h
很容易声明
void\u va\u end(va\u列表)然后让
#定义va_端(ap)__va_端(ap)
。由于
\uuu va\u end
的主体不需要可见,因此不需要有一个可见的
free
声明来释放内存。@HostileFork实际上,如果您的代码不会在一个具有非平凡
va\u end
的平台上运行,那么我可能会忽略这个问题。不过,您可能必须准备一些东西来处理跳转代码中发生的堆分配。