char * vs std :: string in c ++ [geschlossen]

81

Wann sollte ich verwenden std::stringund wann sollte ich char*Arrays von chars in C ++ verwalten?

Es scheint, dass Sie verwenden sollten, char*wenn Leistung (Geschwindigkeit) entscheidend ist und Sie aufgrund der Speicherverwaltung bereit sind, ein riskantes Geschäft zu akzeptieren.

Gibt es andere zu berücksichtigende Szenarien?

jww
quelle

Antworten:

56

Sie können std :: Strings als Referenz übergeben, wenn sie groß sind, um ein Kopieren zu vermeiden, oder einen Zeiger auf die Instanz, sodass ich keinen wirklichen Vorteil bei der Verwendung von Zeichenzeigern sehe.

Ich benutze std :: string / wstring für mehr oder weniger alles, was tatsächlicher Text ist. char *ist jedoch für andere Datentypen nützlich, und Sie können sicher sein, dass die Zuordnung wie gewünscht aufgehoben wird. Ansonsten ist std :: vector der richtige Weg.

Es gibt wahrscheinlich Ausnahmen von all dem.

Skurmedel
quelle
8
Gibt es einen Leistungsunterschied vor den beiden?
vtd-xml-author
3
@ vtd-xml-author: Einige vielleicht. Gerade char *hat fast keinen Overhead. Was genau der Overhead std::stringist, weiß ich nicht, es ist wahrscheinlich implementierungsabhängig. Ich erwarte kaum, dass der Overhead viel größer ist als der eines bloßen Zeichenzeigers. Da ich keine Kopie des Standards besitze, kann ich keine Garantien des Standards detailliert beschreiben. Jeder Leistungsunterschied hängt wahrscheinlich von den auszuführenden Vorgängen ab. std::string::sizekönnte die Größe neben den Zeichendaten speichern und somit schneller sein als strlen.
Skurmedel
2
Warum nicht std :: string für Nicht-Textdaten verwenden? Sie sind nicht nullterminiert, daher sollten Sie in der Lage sein, alles zu speichern, was Sie möchten.
Casey Rodarmor
1
@rodarmor Sie können alles speichern, was Sie möchten, obwohl es ein wenig riskant ist, da die Zeichenfolge für nullterminierte Zeichenfolgen ausgelegt ist. Sie müssen darauf achten, nur binärsichere Operationen zu verwenden, z . B. append(const string&)und append(const char*, size_t)anstelle von operator+=().
Boycy
6
Bist du sicher? Ich weiß, dass viele Operationen davon ausgehen, dass ein char * eine nullterminierte Zeichenfolge ist, aber ich kann mir keine vorstellen, die davon ausgehen, dass eine std :: string keine Nullen enthält.
Casey Rodarmor
57

Mein Standpunkt ist:

  • Verwenden Sie niemals char *, wenn Sie keinen "C" -Code aufrufen.
  • Verwenden Sie immer std :: string: Es ist einfacher, benutzerfreundlicher, optimiert, Standard, verhindert Fehler, es wurde überprüft und funktioniert nachweislich.
Gal Goldman
quelle
13

Verwendung von Rohzeichenfolgen

Ja, manchmal kann man das wirklich. Wenn Sie const char *, char-Arrays, die auf dem Stack zugewiesen sind, und String-Literale verwenden, können Sie dies so tun, dass überhaupt keine Speicherzuweisung erfolgt.

Das Schreiben eines solchen Codes erfordert oft mehr Nachdenken und Sorgfalt als die Verwendung von Zeichenfolgen oder Vektoren, kann jedoch mit geeigneten Techniken durchgeführt werden. Mit den richtigen Techniken kann der Code sicher sein, aber Sie müssen immer sicherstellen, dass Sie beim Kopieren in char [] entweder einige Garantien für die Länge der zu kopierenden Zeichenfolge haben oder dass Sie übergroße Zeichenfolgen ordnungsgemäß prüfen und handhaben. Wenn Sie dies nicht tun, hat die strcpy-Funktionsfamilie den Ruf, unsicher zu sein.

Wie Vorlagen beim Schreiben sicherer Zeichenpuffer helfen können

In Bezug auf die Sicherheit von char [] -Puffern können Vorlagen hilfreich sein, da sie eine Kapselung für die Behandlung der Puffergröße für Sie erstellen können. Vorlagen wie diese werden z. B. von Microsoft implementiert, um strcpy sicher zu ersetzen. Das Beispiel hier ist aus meinem eigenen Code extrahiert, der echte Code hat viel mehr Methoden, aber dies sollte ausreichen, um die Grundidee zu vermitteln:

template <int Size>
class BString
{
  char _data[Size];

  public:
  BString()
  {
    _data[0]=0;
    // note: last character will always stay zero
    // if not, overflow occurred
    // all constructors should contain last element initialization
    // so that it can be verified during destruction
    _data[Size-1]=0;
  }
  const BString &operator = (const char *src)
  {
    strncpy(_data,src,Size-1);
    return *this;
  }

  operator const char *() const {return _data;}
};

//! overloads that make conversion of C code easier 
template <int Size>
inline const BString<Size> & strcpy(BString<Size> &dst, const char *src)
{
  return dst = src;
}
Suma
quelle
1
+1 für "Wenn Sie const char *, char-Arrays, die auf dem Stapel zugewiesen sind, und Zeichenfolgenliterale verwenden, können Sie dies so tun, dass überhaupt keine Speicherzuweisung erfolgt." Die Leute vergessen, dass die Stapelzuweisung viel schneller ist als der Heap.
NoSenseEtAl
char*Zeichenfolgen sind nicht immer auf dem Stapel. char *str = (char*)malloc(1024); str[1024] = 0;
Cole Johnson
@ColeJohnson Ich behaupte nicht, ich meine nur, wenn Sie möchten, dass Ihre Zeichenfolge stapelweise zugewiesen wird, müssen Sie const char * in Verbindung mit Zeichenfolgenliteralen verwenden, nicht std :: string.
Suma
9

Eine Gelegenheit, die Sie verwenden MÜSSEN char*und nicht, std::stringist, wenn Sie statische Zeichenfolgenkonstanten benötigen. Der Grund dafür ist, dass Sie keine Kontrolle über die Reihenfolge haben, in der Module ihre statischen Variablen initialisieren, und ein anderes globales Objekt aus einem anderen Modul möglicherweise auf Ihre Zeichenfolge verweist, bevor es initialisiert wird. http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Static_and_Global_Variables

std::string Profis:

  • verwaltet den Speicher für Sie (der String kann wachsen und die Implementierung weist Ihnen einen größeren Puffer zu)
  • Übergeordnete Programmierschnittstelle, funktioniert gut mit dem Rest von STL.

std::stringNachteile: - Zwei unterschiedliche STL-Zeichenfolgeninstanzen können nicht denselben zugrunde liegenden Puffer gemeinsam nutzen. Wenn Sie also den Wert übergeben, erhalten Sie immer eine neue Kopie. - Es gibt einige Leistungseinbußen, aber ich würde sagen, wenn Ihre Anforderungen nicht besonders sind, ist dies vernachlässigbar.

thesamet
quelle
Tatsächlich implementieren STL-Implementierungen häufig eine Copant-on-Write-Semantik für std :: string, sodass die Übergabe als Wert überhaupt nicht viel kostet. Trotzdem ist es besser, sich nicht darauf zu verlassen, und im Allgemeinen ist es sowieso besser, einen Verweis auf const zu übergeben.
1
Einige std :: string-Implementierungen gaben die COW-Implementierung auf. Darüber hinaus ist es nicht so trivial, wie es scheint, eine (POSIX) thread-sichere Klasse bereitzustellen, die mit dem Standard kompatibel ist. Siehe groups.google.fr/group/ifi.test.boa/browse_frm/thread/… oder groups.google.fr/group/comp.programming.threads/browse_frm/…
Luc Hermitte
8

Sie sollten die Verwendung char*in folgenden Fällen in Betracht ziehen :

  • Dieses Array wird im Parameter übergeben.
  • Sie kennen im Voraus die maximale Größe Ihres Arrays (Sie kennen es ODER Sie legen es fest).
  • Sie werden keine Transformation für dieses Array durchführen.

Tatsächlich werden sie in C ++ char*häufig für feste kleine Wörter wie Optionen, Dateinamen usw. verwendet.

Jérôme
quelle
3
Das Array wird nicht übergeben, ein Zeiger auf das Array. Das ist ein Zeiger - ein Zeiger auf ein Objekt.
Cole Johnson
5

Wann wird ein c ++ std :: string verwendet?

  • Zeichenfolgen sind insgesamt sicherer als char *. Wenn Sie Dinge mit char * tun, müssen Sie normalerweise überprüfen, ob die Dinge richtig sind. In der Zeichenfolgenklasse wird dies alles für Sie erledigt.
  • Wenn Sie char * verwenden, müssen Sie normalerweise den von Ihnen zugewiesenen Speicher freigeben. Dies müssen Sie nicht mit string tun, da der interne Puffer bei Zerstörung freigegeben wird.
  • Strings funktionieren gut mit C ++ - Stringstream. Formatierte E / A sind sehr einfach.

Wann wird char * verwendet?

  • Mit char * haben Sie mehr Kontrolle darüber, was "hinter" den Kulissen passiert, sodass Sie die Leistung bei Bedarf optimieren können.
user88637
quelle
3

Verwenden Sie (const) char * als Parameter, wenn Sie eine Bibliothek schreiben. std :: string-Implementierungen unterscheiden sich zwischen verschiedenen Compilern.

Nemanja Trifunovic
quelle
Wenn Sie eine Bibliothek in C ++ schreiben, müssen Sie sich nicht nur um das Layout von std :: string kümmern. Es gibt viele mögliche Inkompatibilitäten zwischen zwei Implementierungen. Verwenden Sie Bibliotheken in C ++ nur, wenn sie im Quellcode verfügbar sind oder für den genauen Compiler kompiliert wurden, den Sie verwenden. C-Bibliotheken sind normalerweise portabler, aber in diesem Fall haben Sie ohnehin keinen std :: string.
David Thornley
Zwar ist std :: string nicht das einzige Problem, aber es ist etwas zu viel, um zu dem Schluss zu kommen, dass "Bibliotheken in C ++ nur verwendet werden, wenn sie in der Quelle verfügbar sind oder für den genauen Compiler kompiliert wurden, den Sie verwenden." Es gibt Komponentensysteme, die mit verschiedenen Compilern (z. B. COM) einwandfrei funktionieren, und es ist möglich, eine C-Schnittstelle einer Bibliothek zugänglich zu machen, die intern mit C ++ geschrieben wurde (z. B. Win32-API)
Nemanja Trifunovic,
2

Wenn Sie C-Bibliotheken verwenden möchten, müssen Sie sich mit C-Strings befassen. Gleiches gilt, wenn Sie Ihre API C aussetzen möchten.

n0rd
quelle
2

Sie können davon ausgehen, dass die meisten Operationen an einem std :: string (z. B. z. B. find) so optimiert wie möglich sind, sodass sie wahrscheinlich mindestens so gut wie ein reines C-Gegenstück funktionieren .

Es ist auch erwähnenswert, dass std :: string-Iteratoren häufig Zeigern in das zugrunde liegende char-Array zugeordnet werden. Daher ist jeder Algorithmus, den Sie über Iteratoren entwickeln, in Bezug auf die Leistung im Wesentlichen identisch mit demselben Algorithmus über char *.

Beachten Sie beispielsweise operator[]Folgendes: Die meisten STL-Implementierungen führen keine Grenzwertprüfung durch und sollten dies in dieselbe Operation für das zugrunde liegende Zeichenarray übersetzen. AFAIK STLPort kann optional eine Grenzwertprüfung durchführen. Zu diesem Zeitpunkt wäre dieser Operator etwas langsamer.

Was bringt Ihnen die Verwendung von std :: string? Es befreit Sie von der manuellen Speicherverwaltung. Das Ändern der Größe des Arrays wird einfacher, und Sie müssen im Allgemeinen weniger über die Freigabe von Speicher nachdenken.

Wenn Sie sich beim Ändern der Größe eines Strings Sorgen um die Leistung machen, gibt es eine reserveFunktion, die Sie möglicherweise nützlich finden.


quelle
1

Wenn Sie das Zeichenarray in ähnlichem Text usw. verwenden, verwenden Sie std :: string flexibler und benutzerfreundlicher. Wenn Sie es für etwas anderes wie Datenspeicherung verwenden? Arrays verwenden (Vektoren bevorzugen)

RvdK
quelle
1

Auch wenn die Leistung entscheidend ist, sollten Sie sie besser nutzen vector<char>- sie ermöglicht die Speicherzuweisung im Voraus (Reserve () -Methode) und hilft Ihnen, Speicherlecks zu vermeiden. Die Verwendung von vector :: operator [] führt zu einem Overhead, aber Sie können die Adresse des Puffers jederzeit extrahieren und genau so indizieren, als wäre es ein Zeichen *.

scharfer Zahn
quelle
Es wäre jedoch schön, eine typische Zeichenfolgenfunktionalität zu verwenden und nur die Option zu haben, die Richtlinie für den Speicher anzugeben. Siehe dazu den Link in meiner Antwort.
Anonym
Es ist nicht wirklich wahr. Wenn Sie berücksichtigen, dass der Vektor im zusammenhängenden Speicherbereich zugewiesen wird, ist die Neuzuweisung (um die Vektorgröße zu erhöhen) überhaupt nicht effizient, da dies die Kopie des vorherigen Blocks impliziert.
Jérôme
Ich habe Ihre Antwort falsch verstanden, da Sie den Vektor anstelle von char * und nicht anstelle von string verwenden ... In diesem Fall stimme ich zu.
Jérôme
Die Verwendung von operator [] sollte keinen Overhead verursachen. Siehe zum Beispiel stackoverflow.com/questions/381621/…
Luc Hermitte
-1

AFAIK implementiert intern die meisten std :: string-Kopien beim Schreiben, referenzgezählte Semantik, um Overhead zu vermeiden, selbst wenn Strings nicht als Referenz übergeben werden.

piotr
quelle
5
Dies ist nicht mehr der Fall, da das Kopieren beim Schreiben in einer Multithread-Umgebung schwerwiegende Skalierbarkeitsprobleme verursacht.
Suma
Dies gilt zumindest für die Implementierung der STL durch GCC.