C++ 标准参数:多次调用va_start

C++ Standard Args : multiple calls to va_start

本文关键字:调用 va start 标准 参数 C++      更新时间:2023-10-16

我注意到在两个函数中连续调用时va_start存在一些问题。一个基本的例子是以下一个:

std::string format(std::string fmt, ...)
{
   char buf[2000];
   va_list aq;
   va_start(aq, fmt);
   vsprintf(buf, fmt.c_str(), aq);
   va_end(aq);
   return std::string(buf);
}
void error(std::string fmt, ...)
{
   va_list ap;
   va_start(ap, fmt);
   printf("%s", format(fmt, ap).c_str());
   va_end(ap); 
   exit(1);
}
int main()
{
   int x = 10;
   printf("%s", format("Test %dn", x).c_str());
   error("Test %dn", x);
}

生产

Test 10
Test -1078340156

似乎在使用error函数时,参数已损坏。

va_list传递给另一个函数的正确方法是什么?

您可以将va_list作为参数显式传递。 将va_list传递给采用多个参数的函数不会"解压缩"这些参数。 相反,它只是使用两个参数调用函数,其中第二个是va_list。 你从函数中获取垃圾的原因是它试图将该va_list解释为printf的参数之一,从而导致未定义的行为。

这就是为什么有像vsprintf这样的函数 - 这样像printfsprintf这样的函数可以在给定参数va_list的情况下在内部调用帮助程序函数来执行格式化。

例如:

std::string vformat(std::string fmt, va_list args) {
   char buf[2000];
   vsprintf(buf, fmt.c_str(), args);
   return std::string(buf);
}
void error(std::string fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
   printf("%s", vformat(fmt, ap).c_str());
   va_end(ap); 
   exit(1);
}

不过,也就是说,在C++中,您应该使用可变参数模板来执行此操作,因为这些模板可以正确转发,完全类型安全,并且(如果正确实现它)不会有缓冲区溢出的风险。

希望这有帮助!