12

典型例子:

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);
}

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

4

3 回答 3

4

C++ 标准遵循 C 标准来规范va_startet al。C标准有这样的说法:

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

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

是的,包裹起来bartry/catch有所帮助。

于 2013-07-18T18:23:02.837 回答
3

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

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

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

因此,如果bar抛出,您将无法执行va_end并且您的程序具有未定义的行为。如果您添加一个 try/catch 以确保va_end始终按要求调用它,那么您应该没问题。但请记住,您不能将非 POD 作为可变参数传递,因此如果您需要处理它们,无论如何您都需要一种替代机制。

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

于 2013-07-18T18:25:46.070 回答
0

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

我不建议使用它,但有时需要一些疯狂的东西来支持遗留代码。如果你可以在那里使用try catch,那么一定要这样做。

于 2013-07-18T18:33:03.170 回答