1

我正在尝试为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*解决问题?或者它似乎只是被解决了?

4

2 回答 2

3

您不能使用引用类型作为 va_start 的参数。这就是为什么改变为char*工作,所以会离开string但没有&. 使用引用会破坏为获得可变数量的参数而完成的魔法。

请参阅是否有使用带有引用参数的可变参数的陷阱

于 2012-06-18T18:40:26.093 回答
1

你不能那样做。我的意思是,你说的“有效”的版本。

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

如果你的库提供了缓冲区,你可能会有更好的运气vsnprintf,它知道缓冲区有多大。

于 2012-06-18T18:59:01.397 回答