在容易出错的代码中使用va_list是否安全?

时间:2013-07-18 18:15:00

标签: c++

典型示例:

void foo(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);

    // might throw, might not.  who knows.
    bar(fmt, args);

    // uh-oh...
    va_end(args);
}

这是一个坏主意,即在c ++中使用va_list是不常见的吗?如果我将bar包裹在try-catch中,这有帮助吗?会有什么替代方案?

3 个答案:

答案 0 :(得分:4)

C ++标准遵循va_start等规范的C标准。 C标准有这样说:

7.15.1p1 ... va_start和va_copy宏的每次调用都应与相同函数中va_end宏的相应调用相匹配。

因此,如果您在调用va_start之后但在va_end之前以任何方式退出该函数,则您的程序会显示未定义的行为。

是的,将bar包裹在try/catch中会有所帮助。

答案 1 :(得分:3)

C ++标准将此推迟到C标准。

C99(草案)7.15.1 / 1告诉我们:

  

每次调用va_start和va_copy宏都应匹配   在同一个函数中相应调用va_end宏。

因此,如果bar抛出,则无法执行va_end并且您的程序具有未定义的行为。如果您添加try / catch以确保始终根据需要调用va_end,那么您应该没问题。但请记住,你不能将非POD作为varargs传递,所以如果你需要处理它们,你还是需要一种替代机制。

更像C ++的替代方案可能是插入运算符(operator<<),如语言提供的各种iostream所示。

答案 2 :(得分:0)

如上所述,它是c标准的未定义行为。 然而,根据你的平台,marco可以编译成不同的东西,我看到它例如它只是args = 0;而va_list是一个char *;在这种情况下,似乎结束宏不做任何关键的事情。没有什么不好的事情发生,除非我不确定谁会解除分配,但我不知道他们在哪里被分配。

我不以任何方式推荐使用它,但有时候疯狂的东西是支持遗留代码所必需的。如果你可以使用try catch那么一定要这样做。