sprintf mit std::string?
-
Ja, ich bin mir da sicher.
Das Problem ist, dass undefiniertes Verhalten auch bedeuten kann, dass kein Fehler auftritt. Wenn du in uninitialisierten Speicher schreibst, der aber nicht weiter verwendet wird, dann tritt auch kein Fehler auf.strTest += "Hallo du da!";Hier wird durchaus genügend Speicher alloziert um den String aufnehmen zu können. Das macht der Operator += der string-Klasse schon selbst.
Fehler kann man schlecht provozieren. Wenn das programm größer wird, steigt allerdings die Wahrscheinlichkeit, dass es kracht.
Du kannst doch sprintf auf normalen ordentlich initialisierten char* anwenden.
-
Braunstein schrieb:
Du kannst doch sprintf auf normalen ordentlich initialisierten char* anwenden.
Könnte ich, wenn ich wollte. Allerdings ist die funktion einfach nicht sicher, da man vorher nicht weiß wie viel speicher man genau benötigt. Wenn schon dann snprintf, aber die std::string funktionen sind schon schöner, mit ausnahme dass so eine einfach formatierungsfunktion wie sprintf fehlt!
Bisher habe ich um diese ganzen probleme (fehlende sprintf funktion, kein mfc, möglichst kein overhead) immer die CStdString Klasse verwendet. Die ist echt gut, mit ausnahme, dass sie nicht (auf jeden fall bisher noch nicht) für 64 Bit Code geeignet ist. Ich bräuchte eine Klasse die dies alles schafft. Kennst du da was?
gruß
c++frager
-
Das Problem mit dem Speicher läßt sich sicherlich lösen. Dazu kann man aber mangels Info nicht viel sagen.
Diese CStdString kenne ich jetzt nicht.
Meinst du mit 64 Bit Code Unicode? Mir wäre sonst nicht direkt klar, wozu man bei Strings 64Bit braucht.
-
Braunstein schrieb:
Das Problem mit dem Speicher läßt sich sicherlich lösen. Dazu kann man aber mangels Info nicht viel sagen.
Naja es lässt sich fast immer lösen, nur mit welchem aufwand?
Braunstein schrieb:
Meinst du mit 64 Bit Code Unicode? Mir wäre sonst nicht direkt klar, wozu man bei Strings 64Bit braucht.
Mit 64 Bit meinte ich schon 64 Bit. Hat ja auch nichts mit strings direkt zu tun, sondern mit der string-klasse die man verwendet. Die CStdString Klasse erzeugt bei mir nämlich einen ganzen haufen warungen, wenn ich unter vs 7.1 die entdeckung für 64 bit portabilitäts "fehler" einschalte, heißt wenn ich meinen code als 64 bit code kompilieren will, funzt das nicht.
gruß
c++frager
-
Braunstein schrieb:
Du erzeugst hier undefiniertes Verhalten, da du in einen nicht initialisierten Speicherbereich schreibst.
Das ist nicht Problem. Vielmehr ist die Frage, ob überhaupt genug Speicher reserviert wurde.
Das eigentliche Problem ist, dass die constness des c_str() Rückgabewertes weggecastet wurde. Das ist nicht erlaubt. Und in dem Zusammenhang kann ich nur auf meine Sig verweisen.
Und selbst wenn man mittels &strTest[0] auf den Speicher zugreift, man sollte dabei immer beachten, dass nicht mehr als strTest.length() Zeichen geschrieben werden dürfen.c++frager schrieb:
Bist du dir da sicher? Es funktioniert nämlich bisher immer, egal was ich mache.
Nur weil etwas funktioniert, bedeutet dies nicht zwangsläufig, dass es auch legal ist. Und nur legaler Code ist Voraussetzung, dass er auch funktionstüchtig ist.
c++frager schrieb:
zunächst ich verwende kein MFC und kein iostream (wegenm overhead).
Schon mal Stringstreams probiert und getestet, ob diese ebenfalls zu viel Overhead haben?
Ansonsten kann man, sofern du es nicht übertreibst, Stringstream Funktionalität relativ einfach nachbauen. Dafür kannst du dann ja sprintf nutzen, aber nur intern. Generell solltest du in C++ jedenfalls von der Benutzung der ...printf Funktionen absehen.
-
groovemaster schrieb:
Schon mal Stringstreams probiert und getestet, ob diese ebenfalls zu viel Overhead haben?
Erster beitrag von google für "stringstream":
http://www.cplusplus.com/ref/iostream/stringstream/-> Ich denke nicht das es weniger overhead erzeugt als iostream

Und zum nachbauen: Wenn ich intern sprintf (snprintf) verwenden sollte, kann ich auch gleich std::string verwenden und überall wo ich formatierung brauche sprintf benutzen. Bringt mir auch nichts. Na ja, muss man wohl friemeln.
Gruß
c++frager
-
Und wie kommst Du darauf, dass iostreams allgemein ein großes Overhead haben? Wegen der paar virtuellen Funktionen? Ich kann mir nicht vorstellen, dass 'stringstream' langsamer ist, als wenn Du ein buffer-sicheres 'sprintf' implementieren würdest.
Das ist nämlich der Knackpunkt bei Dir: Egal, wie Du es drehst, die Verwendung von 'sprintf' wird Dir bei passenden Eingaben einen Bufferüberlauf erzeugen, der das Programm (im besten Fall) crasht.
c++frager schrieb:
Braunstein schrieb:
Du kannst doch sprintf auf normalen ordentlich initialisierten char* anwenden.
Könnte ich, wenn ich wollte. Allerdings ist die funktion einfach nicht sicher, da man vorher nicht weiß wie viel speicher man genau benötigt.
Ah, und was soll daran anders sein, wenn Du 'string' verwendest? Ich sag's Dir: nichts. Woher soll die Stringklasse denn wissen, wieviel Speicher der formatierte String verbrauchen wird?
Das Problem hierbei ist nicht die Verwendung von 'char*' sondern von 'sprintf'. => Verwende 'stringstream'.
-
c++frager schrieb:
Erster beitrag von google für "stringstream":
http://www.cplusplus.com/ref/iostream/stringstream/-> Ich denke nicht das es weniger overhead erzeugt als iostream

Und was hat jetzt die Hierarchie konkret mit Overhead zu tun? Denken heisst nicht wissen.
Und wie ich schon sagte, miss es und entscheide dann.c++frager schrieb:
Und zum nachbauen: Wenn ich intern sprintf (snprintf) verwenden sollte, kann ich auch gleich std::string verwenden und überall wo ich formatierung brauche sprintf benutzen.
Schon mal was von Abstraktion gehört? ...printf hat ja bekanntlich einige Nachteile, Stichwort: Bufferoverrun und Typsicherheit. Es geht nicht darum, das Rad neu zu erfinden, sondern einen weiteren Layer für die notwendige Sicherheit zu implementieren.
-
groovemaster schrieb:
Braunstein schrieb:
Du erzeugst hier undefiniertes Verhalten, da du in einen nicht initialisierten Speicherbereich schreibst.
Das ist nicht Problem. Vielmehr ist die Frage, ob überhaupt genug Speicher reserviert wurde.
Was ja wohl auf das gleiche hinausläuft. Bei diesem Beispiel ist es sicher, dass nicht genügend Speicher reserviert wurde.
groovemaster schrieb:
Das eigentliche Problem ist, dass die constness des c_str() Rückgabewertes weggecastet wurde. Das ist nicht erlaubt. Und in dem Zusammenhang kann ich nur auf meine Sig verweisen.
Das reine casten verursacht den Fehler nicht, sondern macht ihn nur möglich. Desweiteren ist das auch kein C-Cast-Problem. Dieser Cast läßt sich gut auch in C++ ausdrücken.
sprintf( const_cast<char*>(strTest.c_str()), "%i + %i = %i", 5, 5, 5+5 );Das ist natürlich trotzdem unzulässig und ich hab den entsprechenden Passus auch gerade im Standard gefunden.

-
Die Boost-Format-library macht das besser, was sprintf macht.
Alternativ entweder std::ostringstream, was hier schon angesprochen wurde und gar nicht so teuer ist, wie angenommen oder std::vector<char> als Puffer verwenden, wenn es denn unbedingt diese unsägliche snprintf-Funktion sein muß.
Der Speicherbereich von std::string wird bewusst nur als const-Zeiger zurück gegeben. Bei den meisten Implementierungen ist er auch referenzgezählt und macht ein copy-on-write, was den std::string ungeeignet für die Verwendung als Puffer macht. Das Verhalten ist also Implementierungsabhängig. Im günstigsten Fall stürzt das Programm gleich ab, so daß Du Deinen Fehler gleich erkennst.
Soweit ich mal gehört habe, verwendet die M$-Implementierung einen festen Speicherbereich im String-Objekt selbst für kurze Strings und erst bei längeren weicht sie auf den Heap aus. Damit würde dieses c_str()-Verfahren für kurze Strings (was auch immer kurz heißt) funktionieren. Zumindest in der Version. In der nächsten Version kann das dann wieder anders sein.
Übrigens hat std::string noch eine Methode data(). Diese liefert wie c_str() einen const Zeiger auf die Daten, garantiert aber nicht, daß die Daten nullterminiert sind, was in Deinem Fall ja auch egal ist.
Aber um es nochmal deutlich zu machen: das const in einem std::string wegzucasten ist Falsch! Mit welcher Syntax auch immer.
Mit std::vector<char> ginge es so (ungetestet):
const std::vector<char>::size_type max_format_string = 256; // oder so std::vector<char> buffer(max_format_string); char* formatierterString = &buffer[0]; snprintf(formatierterString, max_format_string, "blah blah", ...);Tntnet
-
Braunstein schrieb:
Was ja wohl auf das gleiche hinausläuft.
Nein, Definition und Initialisierung sind zwei verschiedene Paar Schuhe. Wenn du auf etwas schreibend zugreifen willst, ist eine Definition immer notwendig, eine Initialisierung jedoch nicht.
Braunstein schrieb:
Das ist natürlich trotzdem unzulässig und ich hab den entsprechenden Passus auch gerade im Standard gefunden.
Deshalb
Problem.
Es ging mir in erster Linie nicht darum, dass ein C Cast verwendet wurde, sondern generell ein Cast. Dass es kein C++ Cast ist, macht es nur noch doppelt so schlimm.