当抛出异常时,我需要va_end吗

Do I need to va_end when an exception is thrown?

本文关键字:va end 抛出异常      更新时间:2023-10-16

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

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

如果Logger::output抛出,编译器会正确展开堆栈吗?还是需要在catch子句中添加一个带有va_end(args)的try/catch块?这会是RAII'd吗?还是va_end太神奇了?如果可能的话,请提供该标准的参考资料。

不,他们不能。因为它们是宏而不能这样做的理由是愚蠢的。宏可以从构造函数和析构函数中使用,没有任何问题。但是,va_startva_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会导致内存泄漏。

是的,即使va_start/va_end是宏的,也可以使用Boost.ScopeExit进行RAII。

没有va_startva_end是宏。因此,它们不可能是RAII。此外,对于例外情况不需要特别注意。