我有这段代码(总结)......
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的工作方式,但我不确定是怎么回事.
如果你看看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");
这是C++标准(18.7 - 其他运行时支持)所说的va_start()
(强调我的):
ISO C对
va_start()
标头中宏 的第二个参数的限制 在本国际标准中是不同的.该参数
parmN
是函数定义的变量参数列表中最右边的参数的标识符(前一个参数...
). 如果parmN
使用函数,数组或引用类型声明参数,或者使用与传递没有参数的参数时生成的类型不兼容的类型,则行为未定义.
正如其他人所提到的,如果你将它与非直接C项一起使用(甚至可能以其他方式使用),那么在C++中使用varargs是危险的.
那说 - 我仍然一直使用printf()...