C ++变量参数和vsprintf

时间:2012-06-18 18:23:22

标签: c++ string printf variadic-functions

我试图从sprintf为标准cstdio函数编写几个包装器。但是,在运行程序时,我有一些奇怪的行为和访问冲突崩溃。我已将问题简化并在以下代码中重现:

#include <string>
#include <cstdio>
#include <cstdarg>

std::string vfmt(const std::string& format, va_list args)
{
    int size = format.size() * 2;
    char* buffer = new char[size];
    while (vsprintf(buffer, format.c_str(), args) < 0)
    {
       delete[] buffer;
       size *= 2;
       buffer = new char[size];
    }

    return std::string(buffer);
}

std::string fmt(const std::string& format, ...)
{
    va_list args;
    va_start(args, format);
    std::string res = vfmt(format, args);
    va_end(args);
    return res;
}

int main()
{
    std::string s = fmt("Hello %s!", "world");
    printf(s.c_str());
    return 0;
}

此代码在vsprintf中调用vfmt时会产生内存访问冲突。但是,当我将fmt的函数签名从fmt(const std::string& format, ...)更改为fmt(const char* format, ...)时,我不再崩溃,并且一切都按预期工作。为什么会发生这种情况?

为什么将format参数的类型从const std::string&更改为const char*可以解决问题?或者它似乎只是解决了?

2 个答案:

答案 0 :(得分:3)

您不能将引用类型用作va_start的参数。这就是为什么更改为char*有效,因此离开string但没有&。使用引用会混淆为获得可变数量的参数而完成的魔法。

请参阅Are there gotchas using varargs with reference parameters

答案 1 :(得分:1)

你不能这样做。我的意思是,你说的版本&#34;工作&#34;。

vsprintf不允许您检测传入的缓冲区何时太小,因为它不知道它有多大。它会愉快地写出跟随缓冲区的任何对象。这可能会导致访问冲突,它可能会在以后的某个时间崩溃你的程序,它可能会生成一封电子邮件给你的母亲与附加的图片。这是未定义的行为您的重新分配和重试循环无用。

如果您的图书馆提供缓冲区,那么<{1}}可能会更好运,确实知道缓冲区的大小。