Logo BKs Home - Ein neuer Versuch. Logo

C++ FAQs

   
C++ makes it much harder to shoot yourself in the foot, but when you do, it blows off your whole leg.
- Bjarne Stroustrup

In C we had to code our own bugs. In C++ we can inherit them.
-Prof. Gerald Karam

    
   

Einleitung
Was hat es mit diesen C++ FAQs aufsich?  updated 

C++ allgemein
Sollte man Variablen zu Beginn eines Blocks deklarieren?save
Was muss man beim Test auf eof() beim Lesen aus C++ Streams beachten?save
Wie lassen sich Speicher- bzw. Ressourcenlöcher vermeiden?save
Was sind auto_ptr und wie setzt man sie ein?save
Was ist falsch an void main() { /* ... */ } bzw. main() { /* ... */ }?save
Wie macht man aus einer Zahl einen String?save
Sollte man <iostream.h> oder <iostream> verwenden?  updated 
Was ist eine using-Deklaration?save
Was ist eine using-Direktive?save
Warum kann man Methoden von Basisklassen nicht in abgeleiteten Klassen überladen?save
Was bedeutet das Schlüsselwort static?save
Was ist der Unterschied zwischen <name.h> und <cname>? 

C++ speziell
Wird für den folgenden Code ein Standardkonstruktor erzeugt? Werden die Member initialisiert?save
Was besagt die one definition rule (ODR)?  updatedsave
Was versteht man unter "Koenig Lookup"?save
Wann wird ein Standardkonstruktor erzeugt? Und was tut er?save
Was ist die Regel der großen Drei (Law of the big three)?save
Wie kann man Strings case-insensitiv vergleichen?save
Wird für T x = u; der Zuweisungsoperator von T aufgerufen?save
Ist Zuweisung nicht trivialer Objekte immer langsamer als Initialisierung?save
Überladen, Überschreiben, Überdecken - Was ist was? 

Anderes
Was ist richtige Vererbung und was hat dies mit dem Liskov-Prinzip zu tun?save
Was ist das open-closed Principle?save
Was ist schlecht an globalen Variablen?save
Wie kann man den Inhalt eines Verzeichnisses auflisten?  updated 
Was ist const-correctness?save
Was ist das Singleton-Pattern und wie wird es in C++ implementiert?save
Was bedeutet "named return value optimization"?save
Wie erhält man richtige Vererbung ohne Liskov-Prinzip?save
Warum sollten Instanzvariablen immer private sein?save



zurück zum Seitenanfang
   Q: Wie macht man aus einer Zahl einen String?

Viele Wege führen zum Ziel, aber nur ein paar davon sind standardkomform.

Der aktuelle C++ Standard unterstützt drei verschiedene Möglichkeiten eine Zahl in einen String umzuwandeln. Diese Möglichkeiten sind:

  • sprintf
  • std::strstream
  • std::stringstream

Nun wo die Möglichkeiten bekannt sind, wo liegen die Vor- und Nachteile der einzelnen Varianten?

1. sprintf:

void IntToString(int i, char* buffer)
{
    sprintf(buffer, "%d", i);
}

Diese einfache Funktion wandelt den Integer i in einen String um. Die Vorteile dieser Variante liegen in der Einfachheit und in der Geschwindigkeit. Die Funktion ist ein Einzeiler, einfacher geht es wohl kaum. Die hohe Geschwindigkeit kommt daher, dass sprintf in bereits allokierten Speicher schreiben kann. Es ist also kein weitere Memory-Allokierung nötig. Hier liegt aber auch gleich einer der großen Nachteile dieser Methode. Da sprintf keine Bereichsüberprüfungen durchführt, kann es ganz leicht zu einem Pufferüberlauf kommen. Zeilen wie diese:

char puffer[3];
IntToString(1000, puffer);

sind schnell geschrieben, der Fehler aber ist schwer zu finden.

Die variable Argumentenliste von sprintf verhindert die normale Typprüfung. Typfehler werden dadurch zu Laufzeit- anstatt zu Compilezeitfehlern. Da Laufzeitfehler deutlich schwerer zu finden sind als Compilezeitfehler, ist dies ein weiterer großer Nachteil von sprintf.

Der dritte Nachteil von sprintf ist der starre Formatstring. Ein solcher Formatstring ist immer nur für einen bestimmten Typ gültig. Unterschiedliche Zahlentypen erfordern unterschiedliche Formatstrings. Eine "One size fits all"-Lösung (z.B. über ein Template) ist mit sprintf also nicht möglich

.

2.std::strstream:

void IntToString(int i, char* buffer, int len)
{
    ostrstream temp(buffer, len);
    // ends nicht vergessen!
    temp << i << ends;
}

Die std::strstreams sind zwar seit dem C++98 Standard als deprecated eingestuft. Sie gehören aber dennoch zum Standard. Diese Variante ist genauso schnell wie die sprintf-Version, da sie genau wie sprintf auf bereits allokiertem Speicher arbeitet. Außerdem wird hier automatisch sichergestellt, dass nie mehr als len Zeichen geschrieben werden. Natürlich unterliegen die strstreams auch der vollen Typprüfung. Inkompatible Typen werden bereits zur Compilezeit erkannt und angemahnt. Durch die einheitliche Verwendung des <<-Operators, kann man die strstreams auch in einem Template verwenden:

template<class T>
void ZahlToString(T i, char* buffer, int len)
{
    ostrstream temp(buffer, len);
    // ends nicht vergessen!
    temp << i << ends;
}

Der Nachteil der strstreams liegt in der etwas komplizierteren Anwendung gegenüber sprintf und std::stringstream. Ein fehlendes ends führt dazu, dass der Puffer nicht nullterminiert wird. Da die meisten Stringfunktionen aber nur mit einem nullterminierten String korrekt funktionieren, kann dies zu schwer findbaren Fehlern führen.

3. std::stringstreams:

void IntToString(int i, string& res)
{
    ostringstream temp;
    temp << i;
    res = temp.str();
}

Die std::stringstreams ersetzen die als deprecated eingestuften std::strstreams. Die Vorteile dieser Variante liegen ganz eindeutig in der Sicherheit. Da stringstreams und strings eigene dynamischen Puffer verwenden, sind Bereichsüberschreitungen so gut wie unmöglich. Genauso wie std::strstreams sind die std::stringstreams voll typsicher. Durch die Verwendung des <<-Operators ist diese Variante ebenfalls voll templateisierbar. Jeder Typ T der einen << besitzt kann umgewandelt werden. Die hohe Sicherheit dieser Variante führt direkt zu den Nachteilen. Da stringstreams ihre eigene Speicherverwaltung durchführen, sind sie natürlich langsamer als sprintf und strstreams. Außerdem ist ein weiterer Funktionsaufruf nötig um an das Ergebnis der Umwandlung zu kommen.

Fazit:

VarianteVorteileNachteileHeader
sprintf + schnell
+ leicht zu benutzen
- nicht typsicher
- kein Schutz vor Pufferüberlauf
- nicht als Template verwendbar
<stdio>
std::strstream + schnell
+ typsicher
+ als Template verwendbar
- schwieriger zu verwenden
- deprecated
<strstream>
std::stringstreams + dynamische Puffer
+ typsicher
+ als Template verwendbar
- langsamer wegen eigener Pufferung
- schwieriger zu verwenden
<sstream>

Von der Verwendung von sprintf würde ich auf Grund der mangelnden Sicherheit grundsätzlich abraten. Prinzipiell würde ich zu den stringstreams raten. Sollte tatsächlich einmal ein Profilerlauf zeigen, dass ein Programm durch die stringstreams merklich gebremst wird, bieten die strstreams eine gute Alternative.

Literatur:
  • H. Sutter: "The String Formatters of Manor Farm" - C/C++ Users Journal Nov. 2001

   
   
Ihr Feedback ist mir wichtig:
Wie würden Sie diese Antwort bewerten? Als:     
Haben Sie Fragen, Ergänzungen oder Verbesserungen? Schreiben Sie mir: hume@c-plusplus.de
zurück zum Seitenanfang
    Zuletzt aktualisiert am: 13.01.2007 zurück zur Startseite    
    © Benjamin Kaufmann - Fragen, Kritik und Anregungen bitte direkt an mich oder ins Gästebuch