31

私はこのコード(要約)を持っています...

AnsiString working(AnsiString format,...)
{
    va_list argptr;
    AnsiString buff;

    va_start(argptr, format);
    buff.vprintf(format.c_str(), argptr);

    va_end(argptr);
    return buff;
}

そして、可能な場合は参照渡しが好ましいことに基づいて、このように変更しました。

AnsiString broken(const AnsiString &format,...)
{
... the rest, totally identical ...
}

私の呼び出しコードは次のようなものです:-

AnsiString s1, s2;
    s1 = working("Hello %s", "World");
    s2 = broken("Hello %s", "World");

ただし、s1 には「Hello World」が含まれていますが、s2 には「Hello (null)」が含まれています。これは va_start の動作によるものだと思いますが、何が起こっているのか正確にはわかりません。

4

6 に答える 6

43

va_start の展開先を見ると、何が起こっているかがわかります。

va_start(argptr, format); 

となる(大まかに)

argptr = (va_list) (&format+1);

format が値型の場合、すべての可変引数の直前にスタックに配置されます。format が参照型の場合、アドレスのみがスタックに置かれます。参照変数のアドレスを取得すると、引数のアドレスではなく、アドレスまたは元の変数 (この場合は Broken を呼び出す前に作成された一時的な AnsiString) が取得されます。

完全なクラスを渡したくない場合、オプションは、ポインターで渡すか、仮引数を入れることです。

AnsiString working_ptr(const AnsiString *format,...)
{
    ASSERT(format != NULL);
    va_list argptr;
    AnsiString buff;

    va_start(argptr, format);
    buff.vprintf(format->c_str(), argptr);

    va_end(argptr);
    return buff;
}

...

AnsiString format = "Hello %s";
s1 = working_ptr(&format, "World");

また

AnsiString working_dummy(const AnsiString &format, int dummy, ...)
{
    va_list argptr;
    AnsiString buff;

    va_start(argptr, dummy);
    buff.vprintf(format.c_str(), argptr);

    va_end(argptr);
    return buff;
}

...

s1 = working_dummy("Hello %s", 0, "World");
于 2008-10-21T15:25:29.640 に答える
14

これがC++標準(18.7-その他のランタイムサポート)が言っていることですva_start()(私の強調):

va_start()ISO Cがヘッダー のマクロの2番目のパラメーターに課す制限は <stdarg.h>、この国際規格では異なります。パラメーター parmNは、関数定義の変数パラメーターリスト(の直前のパラメーター)の右端のパラメーターの識別子です ...パラメータparmNが関数、配列、参照型で宣言されている場合、またはパラメータがない引数を渡したときに生じる型と互換性のない型で宣言されている場合、動作は未定義です。

他の人が述べているように、C ++でvarargsを使用することは、非ストレートCアイテムで(そしておそらく他の方法でさえ)使用する場合は危険です。

そうは言っても、私は今でも常にprintf()を使用しています...

于 2008-10-21T15:34:32.617 に答える
4

これを望まない理由については、N0695に詳しい分析があります。

于 2008-10-21T15:07:58.380 に答える
2

C++コーディング標準(Sutter、Alexandrescu)によると:

C++ では varargs を使用しないでください。

これらはタイプ セーフではなく、クラス タイプのオブジェクトに対して UNDEFINED の動作をするため、問題が発生する可能性があります。

于 2008-10-21T15:10:00.217 に答える
0

サイドノート:

varargs 引数としてのクラス型の動作は未定義かもしれませんが、私の経験では一貫しています。コンパイラは、クラスのメモリの sizeof(class) をスタックにプッシュします。つまり、擬似コードでは:

alloca(sizeof(class));
memcpy(stack, &instance, sizeof(class);

これが非常に独創的な方法で利用されている非常に興味深い例として、LPCTSTR の代わりに CString インスタンスを varargs 関数に直接渡すことができ、それが機能し、キャストが含まれていないことに注意してください。彼らがどのようにそれを機能させたかを理解するための演習として、読者に任せます。

于 2008-10-21T18:24:17.873 に答える