C++变量参数和 vsprintf

C++ Variable Arguments and vsprintf

本文关键字:vsprintf 参数 变量 C++      更新时间:2023-10-16

我正在尝试为cstdio中的标准sprintf函数编写一些包装器。但是,我在运行程序时遇到了一些奇怪的行为和访问冲突崩溃。我已经简化了问题并在下面的代码中重现了它:

#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;
}

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

为什么将format参数的类型从const std::string&更改为const char*可以解决问题?还是看起来只是解决了?

不能使用引用类型作为参数来va_start。这就是为什么更改为char*有效,因此离开string但没有&也是如此。使用引用会破坏为了获得可变数量的参数而完成的魔术。

请参阅是否有使用带有引用参数的 varargs 的陷阱。

你不能

那样做。 我的意思是,你说的版本"有效"。

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

您可能对

vsnprintf有更好的运气,如果您的库提供它,它确实知道缓冲区有多大。