我有这段代码(总结)......
AnsiString working(AnsiString format,...)
{
va_list argptr;
AnsiString buff;
va_start(argptr, format);
buff.vprintf(format.c_str(), argptr);
va_end(argptr);
return buff;
}
并且,基于在可能的情况下优先考虑参考,我改变了它。
AnsiString broken(const AnsiString &format,...)
{
... the rest, totally identical ...
}
我的主叫代码是这样的: -
AnsiString s1, s2;
s1 = working("Hello %s", "World");
s2 = broken("Hello %s", "World");
但是,s1包含“Hello World”,而s2包含“Hello(null)”。我认为这是由于va_start的工作方式,但我不确定发生了什么。
答案 0 :(得分:40)
如果你看看va_start扩展到什么,你会看到发生了什么:
va_start(argptr, format);
成为(大致)
argptr = (va_list) (&format+1);
如果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");
或
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");
答案 1 :(得分:14)
以下是关于va_start()
(强调我的)的C ++标准(18.7 - 其他运行时支持)所说的内容:
ISO C所施加的限制 第二个参数 标题中的
va_start()
宏<stdarg.h>
在这方面有所不同 国际标准。参数parmN
是标识符 变量中最右边的参数 函数的参数列表 定义(就在之前的那个)...
)。 如果使用函数,数组或引用声明参数parmN
输入,或不是的类型 与结果类型兼容 当传递参数时 没有参数,行为 未定义。
正如其他人所提到的,如果你将它与非直接C项一起使用(甚至可能以其他方式使用),那么在C ++中使用varargs是危险的。
那就是说 - 我仍然一直使用printf()...
答案 2 :(得分:4)
答案 3 :(得分:2)
根据C ++编码标准(Sutter,Alexandrescu):
varargs永远不应该与C ++一起使用:
它们不是类型安全的,并且对类类型的对象具有UNDEFINED行为,这可能会导致您的问题。
答案 4 :(得分:1)
这是我的简单解决方法(使用Visual C ++ 2010编译):
void not_broken(const string& format,...)
{
va_list argptr;
_asm {
lea eax, [format];
add eax, 4;
mov [argptr], eax;
}
vprintf(format.c_str(), argptr);
}
答案 5 :(得分:0)
旁注:
作为varargs参数的类类型的行为可能是未定义的,但它在我的经验中是一致的。编译器将类的内存的sizeof(类)推送到堆栈。即,在伪代码中:
alloca(sizeof(class));
memcpy(stack, &instance, sizeof(class);
对于一个非常有创意的方式使用的一个非常有趣的例子,请注意你可以直接将CString实例代替LPCTSTR传递给varargs函数,并且它可以工作,并且有没有涉及铸造。我把它作为练习留给读者来弄清楚他们是如何做到的。