Frage

Ich habe dieses Stück Code (zusammengefasst) ...

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

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

    va_end(argptr);
    return buff;
}

Und auf der Grundlage, die durch Verweis übergeben wird, wenn möglich, bevorzugt, habe ich es auf diese Weise.

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

Meine Berufung Code ist wie folgt: -

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

Aber, s1 enthält "Hallo Welt", während s2 "Hallo (null)" hat. Ich denke, dies ist die Art und Weise zurückzuführen ist va_start funktioniert, aber ich bin mir nicht ganz sicher, was los ist.

War es hilfreich?

Lösung

Wenn Sie schauen, was va_start erweitert, um, Sie werden sehen, was passiert:

va_start(argptr, format); 

wird (grob)

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

Wenn Format Wert-Typ ist, wird es auf dem Stapel direkt vor allen variadische Argumente platziert. Wenn Format ein Referenztyp ist, nur wird die Adresse auf dem Stapel abgelegt. Wenn Sie die Adresse der Bezugsgröße, nehmen Sie die Adresse erhalten oder die Originalgröße (in diesem Fall eines vorübergehenden Ansi vor Gebrochene erstellt Calling), nicht die Adresse des Arguments.

Wenn Sie nicht um volle Klassen übergeben wollen, sind Ihre Möglichkeiten, entweder durch den Zeiger passieren, oder in einem Dummy-Argumente setzen:

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");

oder

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");

Andere Tipps

Hier ist, was der C ++ Standard (18,7 - Sonstige Laufzeitunterstützung) sagt über va_start() (Hervorhebung von mir):

  

Die Einschränkungen, die ISO C Orte auf   der zweite Parameter auf den   va_start() Makro in Kopf   <stdarg.h> unterscheiden sich in dieser   Internationaler Standard. der Parameter   parmN ist die Kennung der   rechtesten Parameter in den Variablen   Parameterliste der Funktion   Definition (die kurz vor der   ...).    Wenn der Parameter parmN mit einer Funktion deklariert, Array oder Referenz   Typ, oder mit einem Typ, der nicht ist   mit dem Typ kompatibel, die Ergebnisse   beim Passieren ein Argument für die   es gibt keinen Parameter, das Verhalten    nicht definiert ist.

Wie andere erwähnt haben, mit varargs in C ++ gefährlich ist, wenn man es mit nicht-linearer C Elementen verwenden (und möglicherweise auch auf andere Weise).

Das heißt - ich immer noch printf () verwenden, die ganze Zeit ...

Eine gute Analyse, warum Sie dies nicht möchten, in N0695

Nach C ++ Coding Standards (Sutter, Alexandrescu):

varargs sollte nie mit C ++ verwendet werden:

Sie sind nicht sicher geben und haben nicht definiertes Verhalten für Objekte der Klasse-Typ, der wahrscheinlich das Problem verursacht.

Hier ist meine einfache Abhilfe (kompiliert mit Visual C ++ 2010):

void not_broken(const string& format,...)
{
  va_list argptr;
  _asm {
    lea eax, [format];
    add eax, 4;
    mov [argptr], eax;
  }

  vprintf(format.c_str(), argptr);
}

Side Hinweis:

Das Verhalten für Klassentypen wie varargs Argumente kann nicht definiert werden, aber es ist konsequent in meiner Erfahrung. Der Compiler schiebt sizeof (Klasse) des Speichers der Klasse auf den Stapel. Das heißt, in Pseudo-Code:

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

Für ein wirklich interessantes Beispiel für diese in einer sehr kreativen Art und Weise genutzt wird, bemerken, dass Sie können ein CString Instanz anstelle eines LPCTSTR zu einer varargs Funktion übergeben direkt, und es funktioniert, und es gibt kein Casting beteiligt. Ich lasse es als eine Übung für den Leser, herauszufinden, wie sie diese Arbeit gemacht.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top