Rückgabe einer lokalen Variablen



  • Abend,

    ich habe folgenden Code:

    std::string buildStr(char* formatString, ...) { 
       char buffer[300];
       memset(buffer, 0, sizeof(buffer));
       va_list args;
       va_start(args, formatString);
       vsnprintf_s(buffer, 300, _TRUNCATE, formatString, args);
       return buffer;
    }
    

    Ist dieser Code potentiell gefährlich? Irgendwie sieht das für mich faul aus, weil ja buffer eine lokale variable ist. Andererseits benutze ich die Funktion buildStr oft und es gab noch nie Probleme. 😕



  • Ja, dieser Code ist potentiell gefährlich, und zwar aus genau dem Grund, den du beschreibst. Dadurch, dass buffer nach dem Rücksprung aus der Funktion stirbt, hast du keine Garantie, dass der Speicher nicht überschrieben wird. Das ist so, als ob du eine Datei auf deiner Festplatte löschst und darauf vertraust, dass dein Betriebssystem keine neuen Daten über diese Datei schreibt, solange du die Informationen noch brauchst.



  • Hm, ok Danke.
    Aber wieso funktioniert dann bei mir Code wie dieser:

    std::string viewPanelName = buildStr("viewPanel%i", viewPanelIndex);
    

    ?



  • Michael E. schrieb:

    Dadurch, dass buffer nach dem Rücksprung aus der Funktion stirbt, hast du keine Garantie, dass der Speicher nicht überschrieben wird.

    Nicht ganz. Der Rückgabetyp ist std::string , bei der return -Anweisung wird der implizite Konvertierungskonstruktor std::string(const char*) aufgerufen. Aber ich musste auch zwei Mal schauen, bis ich das erkannt habe.

    Ansonsten kenne ich mich mit vsnprintf_s() nicht aus, kann also nicht viel dazu sagen. Wenn du dich schön an die Dokumentation und Preconditions hältst, sollte die buildStr() okay sein.

    Noch was Grundsätzliches: Die variable Argumentliste ist in C++ verpönt, vor allem weil sie keine Typsicherheit bietet und nur mit POD-Objekten arbeitet. Insgesamt handelt man sich viele subtile Fehlerquellen ein, welche durch moderne C++-Alternativen wie Stream-Operatoren behoben worden sind. Okay, die Syntax ist ein wenig anders, aber man muss Prioritäten setzen. Mit C++0x kann man sogar typsichere variable Argumentlisten einsetzen (über Variadic Templates).



  • Nexus schrieb:

    Michael E. schrieb:

    Dadurch, dass buffer nach dem Rücksprung aus der Funktion stirbt, hast du keine Garantie, dass der Speicher nicht überschrieben wird.

    Nicht ganz. Der Rückgabetyp ist std::string , bei der return -Anweisung wird der implizite Konvertierungskonstruktor std::string(const char*) aufgerufen.

    Richtig, es wird ein neues std::string Objekt erzeugt und der buffer-Inhalt wird dort hineinkopiert. Es kann also nichts schlimmes passieren.



  • bleibt noch zu klären, was passiert, wenn buffer zu klein gewählt ist...
    ich würde die größe jedenfalls wesentlich größer setzen - und vermutlich gibt dir vsnprintf_s irgend eine Angabe, ob die Länge ausgereicht hat oder ob irgendetwas schief ging...
    und obwohl implizit konvertiert wird, würde ich die konvertierung explizit hinschreiben.

    bb



  • unskilled schrieb:

    bleibt noch zu klären, was passiert, wenn buffer zu klein gewählt ist...

    Das ist vollkommen richtig, aber eine andere Baustelle 😉

    Lokale Variablen auf diese Art zu verwenden ist auf jeden Fall vollkommen OK, da der Return-Wert kopiert (EDIT: initialisiert) werden muss, bevor der Scope der Funktion vernichtet wird.



  • Nexus schrieb:

    Michael E. schrieb:

    Dadurch, dass buffer nach dem Rücksprung aus der Funktion stirbt, hast du keine Garantie, dass der Speicher nicht überschrieben wird.

    Nicht ganz. Der Rückgabetyp ist std::string , bei der return -Anweisung wird der implizite Konvertierungskonstruktor std::string(const char*) aufgerufen.

    Recht du hast 😉 Ich hab mir einfach nicht die den Funktionskopf angesehen.



  • unskilled schrieb:

    bleibt noch zu klären, was passiert, wenn buffer zu klein gewählt ist...

    Dann muss er den Formatstring so gestalten, dass der Output die Buffergrösse nicht überschreitet. Angeblich geht es, wie weiss ich momentan auch nicht. In der sprintf-Spezifikation steht es irgendwo drin.
    Ach, er benutzt vsnprintf_s. Das hat sowieso eine eingebaute Begrenzung.



  • Heißt das, es ist immer ok eine lokale variable zurückzugeben.
    Jetzt nicht nur in dem Fall weil implizit der Konvertierungskonstruktor aufgerufen wird.
    Wenn ich also ne Funktion hab die int als Rückgabewert erwartet und ich dann ne lokale int variable zurück gebe, ist das also auch ok?

    Danke schonma;-)



  • Ja, solange es nicht als Referenz zurückgegeben wird.

    hustbaer schrieb:

    Lokale Variablen auf diese Art zu verwenden ist auf jeden Fall vollkommen OK, da der Return-Wert kopiert (EDIT: initialisiert) werden muss, bevor der Scope der Funktion vernichtet wird.

    Wenn ich also ne Funktion hab die int als Rückgabewert erwartet und ich dann ne lokale int variable zurück gebe, ist das also auch ok?

    int add(int a, int b)
    {
      c = a + b;
      return c;
    }
    


  • Das ist in soweit gefährlich, dass die Parameter mit den Formatspezifikationen übereinstimmen müssen. Vielleicht erzeugst du keinen Pufferüberlauf, aber es besteht die Möglichkeit, dass du z.B. versuchst, einen char* als double auszugeben, und der double dann eine ungültige Bitkombination aufweist und zu einem Fehler der RTL führt.



  • fr33g schrieb:

    Heißt das, es ist immer ok eine lokale variable zurückzugeben.

    Sofern du bei der Rückgabe ein neues Objekt erstellst, ja. Was nie okay ist, sind Zeiger und Referenzen auf lokale Variablen zurückzugeben, da deren Ziel bei Funktionsende zerstört wird.

    int& get_func()
    {
        int a;
        return a; // böse!
    }
    


  • fr33g schrieb:

    Heißt das, es ist immer ok eine lokale variable zurückzugeben.

    Variablen werden nie zurückgegeben, sondern Werte (inklusive Addressen von Zeigern) oder Referenzen.

    fr33g schrieb:

    Wenn ich also ne Funktion hab die int als Rückgabewert erwartet und ich dann ne lokale int variable zurück gebe, ist das also auch ok?

    Du gibst den Wert der Variablen zurück. Das ist natürlich völlig in Ordnung.



  • Ok danke für die Antworten, jetzt ists mir klar=)


Anmelden zum Antworten