抛出异常时是否需要va_end?

Mar*_*utz 7 c++ printf exception-handling variadic-functions

我有一个基于printf样式格式的日志框架:

void Logger::debug(const char *fmt, ...) {
    va_list args;
    va_start(args, fmt);
    this->output(DebugLevel, fmt, args);
    va_end(args);
}
Run Code Online (Sandbox Code Playgroud)

如果Logger::output抛出,编译器是否会正确展开堆栈,还是需要va_end(args)在catch子句中添加try/catch块?这可能是RAII',还是太神奇va_end了?如果可能,请包括对标准的参考.

小智 6

不,他们不能.他们不能因为它们是宏的原因是愚蠢的.宏可以从构造函数和析构函数中使用而没有任何问题.但是,va_start并且va_end具有必须从同一函数调用它们的特定要求.将它们移动到单独的函数是无效的.C++指的是C标准,C标准说"每个宏va_startva_copy宏的调用都应该通过va_end相同函数中宏的相应调用来匹配".(7.15.1)如果你va_end从助手类的析构函数中调用它,它可能会起作用,也可能不起作用.由于它不符合标准的要求,因此行为未定义.

编辑:至于另一个问题,当你va_end抛出异常时,你是否真的需要一个合法的参数,"调用va_end宏"实际上并不要求代码到达你调用那个宏的点(从那以后)宏调用严格来说只是一个编译时动作),但强烈暗示你确实需要它.所以是的,使用try/ catch如果有例外是可能的.C99的理由简单地注意到,在其描述va_copy的是va_start可分配内存.(我知道它实际上没有实现它的实现.)在这样的实现上,va_end然后释放该内存,因此跳过va_end会导致内存泄漏.


Thi*_*One 0

va_startva_end是宏。因此,它们不能被RAII“编辑”。此外,对于异常情况也不需要特别注意。

  • 对于例外情况*需要*特别照顾。在没有匹配的“va_end”的情况下调用“va_start”后从函数返回会产生未定义的行为。 (4认同)