Ist string :: c_str () in C ++ 11 nicht mehr null-terminiert?

In C ++ 11 ist basic_string::c_str genau gleich wie basic_string::data , das wiederum genau so definiert ist wie *(begin() + n) und *(&*begin() + n) (wenn 0 <= n < size() ).

Ich kann nichts finden, bei dem die Zeichenfolge am Ende immer ein Nullzeichen haben muss.

Bedeutet dies, dass c_str() nicht mehr garantiert eine Null-terminierte Zeichenfolge erzeugt?

Zeichenfolgen müssen jetzt interne Puffer mit Nullabschluss verwenden. Sehen Sie sich die Definition von operator[] (21.4.5) an:

Benötigt: pos < = size() .

Rückgabe: *(begin() + pos) wenn pos < size() , sonst ein Verweis auf ein Objekt vom Typ T mit dem Wert charT() ; Der referenzierte Wert darf nicht verändert werden.

Betrachtet man c_str (21.4.7.1/1), sehen wir, dass es in Form von operator[] :

Rückgabe: Ein pointers p so dass p + i == &operator[](i) für jedes i in [0,size()] .

Und sowohl c_str als auch data müssen O (1) sein, so dass die Implementierung effektiv gezwungen wird, Puffer mit Null- c_str zu verwenden.

Wie David Rodríguez - dribeas in den Kommentaren ausführt , bedeutet die Rückgabewertanforderung außerdem, dass Sie &operator[](0) als Synonym für c_str() , sodass das abschließende Nullzeichen im selben Puffer liegen muss (seit *(p + size()) muss gleich charT() ) sein; Dies bedeutet auch, dass selbst wenn der Terminator langsam initialisiert wird, es nicht möglich ist, den Puffer im Zwischenzustand zu beobachten.

Nun, es stimmt, dass der neue Standard festlegt, dass .data () und .c_str () jetzt Synonyme sind. Es besagt jedoch nicht, dass .c_str () nicht mehr mit Null abgeschlossen ist 🙂

Es bedeutet nur, dass Sie sich darauf verlassen können, dass .data () ebenfalls nullterminiert ist.

In Paper N2668 werden c_str () – und data () – Member von std :: basic_string wie folgt definiert:

  const charT* c_str() const; const charT* data() const; 

Rückgabewerte: Ein pointers auf das Anfangselement eines Arrays der Länge size () + 1, dessen erste size () -Elemente den entsprechenden Elementen der von * this gesteuerten Zeichenfolge entsprechen und deren letztes Element ein von charT () spezifiziertes Nullzeichen ist.

Benötigt: Das Programm darf keine der im Zeichenfeld gespeicherten Werte ändern.

Beachten Sie, dass dies NICHT bedeutet, dass ein gültiger std :: string als C-String behandelt werden kann, da std :: string eingebettete Nullen enthalten kann, die den C-String vorzeitig beenden, wenn sie direkt als const char * verwendet werden.

Nachtrag:

Ich habe keinen Zugang zu der tatsächlich veröffentlichten endgültigen Spezifikation von C ++ 11, aber es scheint, dass der Wortlaut irgendwo in der Überarbeitungshistorie der Spezifikation gelöscht wurde: zB http://www.open-std.org/jtc1/ sc22 / wg21 / docs / papers / 2011 / n3242.pdf

§ 21.4.7 basic_string Zeichenfolgenoperationen [string.ops]

§ 21.4.7.1 basic_string accessors [string.accessors]

  const charT* c_str() const noexcept; const charT* data() const noexcept; 
  1. Rückgabe: Ein pointers p, so dass p + i == &operator[](i) für jedes i in [0,size()] .
  2. Komplexität: konstante Zeit.
  3. Benötigt: Das Programm darf keine der im Zeichenfeld gespeicherten Werte ändern.

Die “Geschichte” war, dass vor langer Zeit, als alle in einzelnen Threads arbeiteten oder zumindest die Threads Arbeiter mit ihren eigenen Daten waren, sie eine String-class für C ++ entwarfen, die die String-Handhabung einfacher als zuvor machte und sie überladen Operator + zum Verketten von Strings.

Das Problem bestand darin, dass Benutzer Folgendes tun würden:

 s = s1 + s2 + s3 + s4; 

und jede Verkettung würde ein temporäres erzeugen, das eine Zeichenkette implementieren musste.

Daher hatte jemand die Geistesblitze von “fauler Bewertung”, so dass man intern eine Art “Seil” mit allen Strings speichern konnte, bis jemand es als C-String lesen wollte. An diesem Punkt würden Sie die interne Repräsentation in einen zusammenhängenden Puffer ändern .

Dies triggerse das oben beschriebene Problem, verursachte jedoch eine Menge anderer Probleme, insbesondere in der Multi-Threading-Welt, in der man erwartete, dass eine .c_str () -Operation schreibgeschützt ist / nichts ändert und daher nichts gesperrt werden muss. Vorzeitiges internes Locking in der classnimplementierung für den Fall, dass jemand Multi-Threading (wenn es nicht einmal einen Threading-Standard gab) gab, war auch keine gute Idee. In der Tat war es teurer, etwas davon zu tun, als einfach den Puffer jedes Mal zu kopieren. Derselbe Grund, dass die “copy on write” -Implementierung für String-Implementierungen aufgegeben wurde.

Eine .c_str() eine wirklich unveränderliche Operation zu machen, erwies sich als die vernünftigste Aufgabe, aber konnte man sich darauf in einem Standard verlassen, der nun Thread-bewusst ist? Daher hat der neue Standard entschieden, klar zu sagen, dass Sie können, und somit muss die interne Darstellung den Nullabschluss halten.

Gut beobachtet. Dies ist sicherlich ein Fehler in dem kürzlich angenommenen Standard; Ich bin mir sicher, dass es keine Absicht gab, den gesamten Code, der c_str verwendet, zu c_str . Ich würde einen comp.std.c++ vorschlagen, oder zumindest die Frage in comp.std.c++ (was normalerweise vor dem Ausschuss endet, wenn es sich um einen Defekt handelt).