有没有使用带参考参数的varargs

Rod*_*ddy 29 c++ reference variadic-functions

我有这段代码(总结)......

AnsiString working(AnsiString format,...)
{
    va_list argptr;
    AnsiString buff;

    va_start(argptr, format);
    buff.vprintf(format.c_str(), argptr);

    va_end(argptr);
    return buff;
}
Run Code Online (Sandbox Code Playgroud)

并且,在可能的情况下优先考虑通过参考,我因此改变了它.

AnsiString broken(const AnsiString &format,...)
{
... the rest, totally identical ...
}
Run Code Online (Sandbox Code Playgroud)

我的主叫代码是这样的: -

AnsiString s1, s2;
    s1 = working("Hello %s", "World");
    s2 = broken("Hello %s", "World");
Run Code Online (Sandbox Code Playgroud)

但是,s1包含"Hello World",而s2包含"Hello(null)".我认为这是由于va_start的工作方式,但我不确定是怎么回事.

Ecl*_*pse 40

如果你看看va_start扩展到什么,你会看到发生了什么:

va_start(argptr, format); 
Run Code Online (Sandbox Code Playgroud)

变得(大致)

argptr = (va_list) (&format+1);
Run Code Online (Sandbox Code Playgroud)

如果format是值类型,它将在所有可变参数之前放置在堆栈上.如果format是引用类型,则只有地址放在堆栈上.当你获取引用变量的地址时,你得到地址或原始变量(在这种情况下是在调用Broken之前创建的临时AnsiString),而不是参数的地址.

如果你不想传递完整的类,你的选择是通过指针传递,或者放入一个伪参数:

AnsiString working_ptr(const AnsiString *format,...)
{
    ASSERT(format != NULL);
    va_list argptr;
    AnsiString buff;

    va_start(argptr, format);
    buff.vprintf(format->c_str(), argptr);

    va_end(argptr);
    return buff;
}

...

AnsiString format = "Hello %s";
s1 = working_ptr(&format, "World");
Run Code Online (Sandbox Code Playgroud)

要么

AnsiString working_dummy(const AnsiString &format, int dummy, ...)
{
    va_list argptr;
    AnsiString buff;

    va_start(argptr, dummy);
    buff.vprintf(format.c_str(), argptr);

    va_end(argptr);
    return buff;
}

...

s1 = working_dummy("Hello %s", 0, "World");
Run Code Online (Sandbox Code Playgroud)

  • 这是众所周知的漏洞抽象的一个很好的例子.va_start看起来像某种神奇的东西,但实际上它只是一些编译器批准的hackery. (7认同)
  • 很好地解释了发生了什么. (2认同)

Mic*_*urr 14

这是C++标准(18.7 - 其他运行时支持)所说的va_start()(强调我的):

ISO C对va_start()标头<stdarg.h>中宏 的第二个参数的限制 在本国际标准中是不同的.该参数 parmN是函数定义的变量参数列表中最右边的参数的标识符(前一个参数 ...). 如果parmN使用函数,数组或引用类型声明参数,或者使用与传递没有参数的参数时生成的类型不兼容的类型,则行为未定义.

正如其他人所提到的,如果你将它与非直接C项一起使用(甚至可能以其他方式使用),那么在C++中使用varargs是危险的.

那说 - 我仍然一直使用printf()...