Bedeutet “&s[0]” auf zusammenhängende Zeichen in einem std::string zeigen?
Lesezeit: 4 Minuten
paxos1977
Ich mache einige Wartungsarbeiten und bin auf etwas wie das Folgende gestoßen:
std::string s;
s.resize( strLength );
// strLength is a size_t with the length of a C string in it.
memcpy( &s[0], str, strLength );
Ich kenne die Verwendung von &s[0] wäre sicher, wenn es ein std::vector wäre, aber ist dies eine sichere Verwendung von std::string?
Die Verwendung von &s[0] ist in Ordnung, memcpy() wohl weniger. Warum führen Sie nicht einfach eine Zuweisung durch oder verwenden die Member-Funktion assign() des Strings?
– anon
31. Dezember 2009 um 20:26 Uhr
@Neil Butterworth, das frage ich mich, wenn ich mir diesen Code ansehe … 😉
– paxos1977
31. Dezember 2009 um 20:29 Uhr
Je mehr Erfahrung Sie mit der Programmierung in C++ sammeln, desto mehr verzichten Sie auf die Verwendung von memset und memcpy, und lernen Sie die Argumentation. Dies ist eine, die Sie zu Ihrer Erfahrung hinzufügen können.
– Thomas Matthäus
31. Dezember 2009 um 21:30 Uhr
Tod Gardner
Die Zuordnung eines std::string ist unter dem C++98/03-Standard nicht garantiert zusammenhängend, aber C++11 erzwingt dies. In der Praxis weder ich noch Herb Sutter kennen eine Implementierung, die keinen zusammenhängenden Speicher verwendet.
Beachten Sie, dass die &s[0] Ding funktioniert garantiert immer nach dem C++11-Standard, selbst im Fall von Zeichenfolgen der Länge 0. Es wäre nicht garantiert, wenn Sie dies tun würden str.begin() oder &*str.begin()aber für &s[0] die Norm definiert operator[] als:
Kehrt zurück: *(begin() + pos) wenn pos < size()andernfalls ein Verweis auf ein Objekt vom Typ T mit Wert charT(); der referenzierte Wert darf nicht verändert werden
Fortsetzung auf, data() ist definiert als:
Kehrt zurück: Ein Zeiger p so dass p + i == &operator[](i) für jeden i in [0,size()].
(beachten Sie die eckigen Klammern an beiden Enden des Bereichs)
Notiz: Vorstandardisierung C++0x hat nicht garantiert &s[0] mit Zeichenfolgen der Länge Null zu arbeiten (eigentlich war es ein explizit undefiniertes Verhalten), und eine ältere Überarbeitung dieser Antwort erklärte dies; Dies wurde in späteren Standardentwürfen behoben, daher wurde die Antwort entsprechend aktualisiert.
Ich habe den Standard in den letzten Monaten nicht befolgt, aber ich hatte den Eindruck, dass dies noch im 0x-Entwurf war und daher noch nicht erforderlich ist (oder werden wird, wenn eine Bibliothek nur ’03 implementiert).
– Todd Gardner
31. Dezember 2009 um 20:37 Uhr
Sutter sagt in einem Kommentar zu diesem Beitrag: „Aktuelles ISO C++ erfordert &str[0] um einen Zeiger auf zusammenhängende Zeichenfolgendaten (aber nicht unbedingt nullterminiert!) Auszuhusten, “was die Verwendung des OP tatsächlich korrekt machen würde. Ich kann jedoch nichts finden, was das im Standard sagt (zumindest ist es nicht in 21.3.4 lib.string.access).
– James McNellis
31. Dezember 2009 um 20:37 Uhr
Ich denke, das könnte richtig sein; der Standardfehler 530 sagt Bediener[] ist zusammenhängend, aber das Iterator-Interface ist nicht garantiert, und zitiert 23.4.4. Ich grabe meinen Standard aus, um ihn zu überprüfen.
– Todd Gardner
31. Dezember 2009 um 20:47 Uhr
Ich habe den Fehlerlink in Sutters Beitrag direkt übersprungen, deshalb habe ich ihn übersehen. In jedem Fall sagt der Fehler “wir benötigen schon fast Kontiguität” (Schlüsselwort: fast) und ich sehe nicht, wie sein Verweis auf Multiset relevant ist (basic_string ist eine Sequenz mit Iteratoren mit wahlfreiem Zugriff). Ich denke jedoch, dass es wichtig ist, dass “angesichts der Existenz von data() und der Definition von operator[] und was die Daten betrifft, glaube ich nicht, dass es möglich ist, einen nützlichen und standardkonformen basic_string zu schreiben, der nicht zusammenhängend ist.”
– James McNellis
31. Dezember 2009 um 21:11 Uhr
James: das fast ist, weil das null für ist s[s.length()] muss nicht zusammenhängend sein. &s[n] + 1 == &s[n + 1] muss für alle n wo wahr sein 0 <= n < s.length() - 1. Die Forderung ist in 21.3.4/1 begraben s[n] muss dasselbe Objekt wie zurückgeben s.data()[n] (für n < length()) und data() müssen zusammenhängend sein.
– Roger Pate
31. Dezember 2009 um 21:39 Uhr
Technisch gesehen nein, da std::string ist nicht erforderlich, seinen Inhalt zusammenhängend im Speicher zu speichern.
In fast allen Implementierungen (jeder Implementierung, die mir bekannt ist) werden die Inhalte jedoch zusammenhängend gespeichert, und dies würde “funktionieren”.
Können Sie einige Implementierungen identifizieren, bei denen es nicht funktionieren würde?
– Rob Kennedy
31. Dezember 2009 um 20:25 Uhr
Nö. Aber Sie könnten eine solche Implementierung vornehmen, wenn Sie wollten.
– James McNellis
31. Dezember 2009 um 20:25 Uhr
@Neil: Hast du einen Link/Verweis zu diesem TC?
– James McNellis
31. Dezember 2009 um 20:35 Uhr
Aargh – Entschuldigung, Gehirn geht – ich denke an Vektor, nicht an String. Entschuldigung rundum.
– anon
31. Dezember 2009 um 20:39 Uhr
Kein Problem. Ich bin immer noch neugierig, wovon Sutter spricht &str[0]obwohl (vgl. meinen Kommentar zu Todds Antwort).
– James McNellis
31. Dezember 2009 um 20:42 Uhr
Sebkrämer
Es ist sicher zu verwenden. Ich denke, die meisten Antworten waren einmal richtig, aber der Standard hat sich geändert. Zitieren aus dem C ++ 11-Standard, basic_string allgemeine Anforderungen [string.require]21.4.1.5, sagt:
Die char-ähnlichen Objekte in einem basic_string-Objekt müssen zusammenhängend gespeichert werden. Das heißt, für jedes basic_string-Objekt s soll die Identität &*(s.begin() + n) == &*s.begin() + n für alle Werte von n gelten, so dass 0 <= n < s.size ().
Etwas davor heißt es, dass alle Iteratoren Random-Access-Iteratoren sind. Beide Bits unterstützen die Verwendung Ihrer Frage. (Außerdem verwendet Stroustrup es anscheinend in seinem neuesten Buch 😉 )
Es ist nicht unwahrscheinlich, dass diese Änderung in C++11 vorgenommen wurde. Ich meine mich zu erinnern, dass die gleiche Garantie damals für Vektor hinzugefügt wurde, was auch sehr nützlich wurde Daten() Zeiger mit dieser Version.
Ich hoffe, das hilft.
Die Frage war vor c++11 (sie ist als solche gekennzeichnet). Sie haben Recht, c ++ 11 hat es offiziell sicher gemacht, dies zu tun.
– paxos1977
21. Januar 2015 um 20:08 Uhr
John Dibling
Leser sollten beachten, dass diese Frage 2009 gestellt wurde, als der C++03-Standard die aktuelle Veröffentlichung war. Diese Antwort basiert auf der Version des Standards, in der std::strings sind nicht garantiert die Nutzung zusammenhängenden Speichers. Da diese Frage nicht im Zusammenhang mit einer bestimmten Plattform (wie gcc) gestellt wurde, mache ich keine Annahmen über die Plattform von OP – insbesondere, ob sie zusammenhängenden Speicher für die verwendet hat oder nicht string.
Gesetzlich? Vielleicht, vielleicht nicht. Sicher? Wahrscheinlich, aber vielleicht auch nicht. Guter Code? Nun, lass uns nicht dorthin gehen …
Dies ist im Allgemeinen nicht sicher, unabhängig davon, ob die interne Saitenfolge dauerhaft gespeichert ist oder nicht. Es kann viele andere Implementierungsdetails geben, die sich darauf beziehen, wie die gesteuerte Sequenz gespeichert wird std::string Objekt, neben der Kontinuität.
Ein echtes praktisches Problem dabei könnte folgendes sein. Die kontrollierte Abfolge von std::string muss nicht als nullterminierter String gespeichert werden. In der Praxis entscheiden sich jedoch viele (die meisten?) Implementierungen dafür, den internen Puffer um 1 zu vergrößern und die Sequenz trotzdem als nullterminierte Zeichenfolge zu speichern, da dies die Implementierung von vereinfacht c_str() Methode: Geben Sie einfach einen Zeiger auf den internen Puffer zurück und Sie sind fertig.
Der Code, den Sie in Ihrer Frage zitiert haben, bemüht sich nicht, die Daten mit Null zu beenden, und sie werden in den internen Puffer kopiert. Möglicherweise weiß es einfach nicht, ob für diese Implementierung von Nullterminierung erforderlich ist std::string. Möglicherweise beruht es darauf, dass der interne Puffer nach dem Aufruf von mit Nullen gefüllt wird resize, so dass das zusätzliche Zeichen, das von der Implementierung für das Null-Terminator zugewiesen wird, praktischerweise auf Null voreingestellt ist. All dies ist ein Implementierungsdetail, was bedeutet, dass diese Technik von einigen ziemlich fragilen Annahmen abhängt.
Mit anderen Worten, in einigen Implementierungen müssten Sie wahrscheinlich verwenden strcpynicht memcpy die Daten so in die kontrollierte Reihenfolge zu zwingen. Während Sie in einigen anderen Implementierungen verwenden müssten memcpy und nicht strcpy.
Nach dem Anruf bei resize Sie können ziemlich sicher sein, dass die interne Zeichenfolge nullterminiert ist oder nicht, wie es die Implementierung erfordert. Nach einem Anruf bei resize Schließlich müssen Sie eine gültige Zeichenfolge mit n Zeichen haben (bei Bedarf mit Nullzeichen aufgefüllt). – Allerdings zeigt es Unverständnis für die std::string Klasse: memcpy wird entweder aus Unwissenheit oder als fehlgeleiteter Leistungsversuch (wegen der resize Der Aufruf des Codes führt dazu, dass dem Puffer zweimal Werte zugewiesen werden).
– Onkel Bens
31. Dezember 2009 um 23:23 Uhr
@UncleBens: Ich verstehe deinen ersten Satz nicht. In jedem Fall garantiert ja der Sprachstandard, dass die Größe zunimmt resize Call füllt die Zeichenfolge mit Nullen auf. Allerdings garantiert die Norm die Polsterung nur bis zur geforderten Größe (strLength in diesem Fall), aber es gibt im Standard keine Garantie für dieses zusätzliche Zeichen, wenn die Implementierung eines zuweist.
– Ant
31. Dezember 2009 um 23:28 Uhr
Ab C++11 ist der interne Puffer leer, wenn die Zeichenfolge nicht leer ist erforderlich nullterminiert sein, weil beides data() und c_str() sind erforderlich um denselben Puffer zurückzugeben, und c_str() ist erforderlich um immer einen Zeiger auf einen nullterminierten Puffer zurückzugeben (data() darf zurückkehren nullptr wenn leer). Vor C++11 war der interne Puffer nicht vorhanden erforderlich nullterminiert (oder sogar zusammenhängend) sein, aber die meisten Implementierungen waren, weil es die Implementierung von vereinfachte c_str()
– Rémy Lebeau
30. August 2019 um 22:56 Uhr
Clifford
Der Code könnte funktionieren, aber mehr Glück als Urteil macht er Annahmen über die Implementierung, die nicht garantiert sind. Ich schlage vor, dass die Bestimmung der Gültigkeit des Codes irrelevant ist, während es sich um eine sinnlose Überkomplikation handelt, die sich leicht auf Folgendes reduzieren lässt:
std::string s( str ) ;
oder wenn Sie einem vorhandenen std::string-Objekt zuweisen, einfach:
s = str ;
und lassen Sie dann std::string selbst bestimmen, wie das Ergebnis erreicht wird. Wenn Sie auf diese Art von Unsinn zurückgreifen, können Sie std::string genauso gut nicht verwenden und dabei bleiben, da Sie alle mit C-Strings verbundenen Gefahren wieder einführen.
Nach dem Anruf bei resize Sie können ziemlich sicher sein, dass die interne Zeichenfolge nullterminiert ist oder nicht, wie es die Implementierung erfordert. Nach einem Anruf bei resize Schließlich müssen Sie eine gültige Zeichenfolge mit n Zeichen haben (bei Bedarf mit Nullzeichen aufgefüllt). – Allerdings zeigt es Unverständnis für die std::string Klasse: memcpy wird entweder aus Unwissenheit oder als fehlgeleiteter Leistungsversuch (wegen der resize Der Aufruf des Codes führt dazu, dass dem Puffer zweimal Werte zugewiesen werden).
– Onkel Bens
31. Dezember 2009 um 23:23 Uhr
@UncleBens: Ich verstehe deinen ersten Satz nicht. In jedem Fall garantiert ja der Sprachstandard, dass die Größe zunimmt resize Call füllt die Zeichenfolge mit Nullen auf. Allerdings garantiert die Norm die Polsterung nur bis zur geforderten Größe (strLength in diesem Fall), aber es gibt im Standard keine Garantie für dieses zusätzliche Zeichen, wenn die Implementierung eines zuweist.
– Ant
31. Dezember 2009 um 23:28 Uhr
Ab C++11 ist der interne Puffer leer, wenn die Zeichenfolge nicht leer ist erforderlich nullterminiert sein, weil beides data() und c_str() sind erforderlich um denselben Puffer zurückzugeben, und c_str() ist erforderlich um immer einen Zeiger auf einen nullterminierten Puffer zurückzugeben (data() darf zurückkehren nullptr wenn leer). Vor C++11 war der interne Puffer nicht vorhanden erforderlich nullterminiert (oder sogar zusammenhängend) sein, aber die meisten Implementierungen waren, weil es die Implementierung von vereinfachte c_str()
– Rémy Lebeau
30. August 2019 um 22:56 Uhr
9715400cookie-checkBedeutet “&s[0]” auf zusammenhängende Zeichen in einem std::string zeigen?yes
Die Verwendung von &s[0] ist in Ordnung, memcpy() wohl weniger. Warum führen Sie nicht einfach eine Zuweisung durch oder verwenden die Member-Funktion assign() des Strings?
– anon
31. Dezember 2009 um 20:26 Uhr
@Neil Butterworth, das frage ich mich, wenn ich mir diesen Code ansehe … 😉
– paxos1977
31. Dezember 2009 um 20:29 Uhr
Je mehr Erfahrung Sie mit der Programmierung in C++ sammeln, desto mehr verzichten Sie auf die Verwendung von
memset
undmemcpy
, und lernen Sie die Argumentation. Dies ist eine, die Sie zu Ihrer Erfahrung hinzufügen können.– Thomas Matthäus
31. Dezember 2009 um 21:30 Uhr