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 の引数として参照型を使用することはできません。そのため、 に変更すると機能し、 but なしでchar*残すこともできます。参照を使用すると、可変数の引数を取得するために行われる魔法が台無しになります。string&

参照パラメーターで varargs を使用する際に問題があるかどうかを参照してください

于 2012-06-18T18:40:26.093 に答える
1

そんなことはできません。つまり、あなたが「動作する」と言うバージョンです。

vsprintf渡されたバッファが小さすぎる場合は、バッファの大きさがわからないため、検出できません。バッファに続くオブジェクトは何でも喜んで上書きします。これにより、アクセス違反が発生したり、後でプログラムがクラッシュしたり、際どい画像が添付された電子メールが母親に送信されたりする可能性があります。これは未定義の動作です。 再割り当てと再試行のループは役に立ちません。

vsnprintfライブラリがバッファを提供している場合は、バッファの大きさを知っている の方がうまくいくかもしれません。

于 2012-06-18T18:59:01.397 に答える