2009-12-31 8 views
31

Ich mache einige Wartungsarbeiten und lief über etwa wie folgt:Zeigt "& s [0]" auf zusammenhängende Zeichen in einer std :: string?

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 weiß, mit & s [0] wäre sicher, wenn es sich um ein std :: vector war, aber das ist eine sichere Anwendung von std :: string?

+3

Die Verwendung von & s [0] ist OK, memcpy() wohl weniger. Warum nicht einfach eine Zuweisung machen oder die member-Funktion assign() benutzen? –

+1

@Neil Butterworth, das frage ich mich beim Betrachten dieses Codes ...;) – paxos1977

+0

Wenn Sie Erfahrung in C++ programmieren, werden Sie mehr und mehr von 'memset' und' memcpy' ablassen und lernen die Begründung. Dies ist einer Ihrer Erfahrungen hinzuzufügen. –

Antwort

34

Eine Zuweisung von std :: string ist nicht garantiert zusammenhängend unter dem C++ 98/03-Standard, aber C++ 11 erzwingt es. In der Praxis kennt weder ich noch Herb Sutter eine Implementierung, die nicht zusammenhängende Speicher verwendet.

Beachten Sie, dass die &s[0] Sache immer von der C++ 11-Standard funktioniert, auch in der 0-Länge Zeichenfolge Fall. Es wäre nicht gewährleistet, wenn Sie str.begin() oder &*str.begin() tun, aber für &s[0] die Norm definiert operator[] als:

Returns: *(begin() + pos) wenn pos < size(), ansonsten eine Referenz auf ein Objekt vom Typ T mit Wert charT(); der referenzierte Wert darf nicht

Weiter auf geändert wird, wird data() wie folgt definiert:

Returns: 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)


Hinweis: Vornormenbereich C++ 0x nicht &s[0] garantieren mit Null-Zeichenfolgen zu arbeiten (tatsächlich, es war 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.

+0

Ich habe den Standard für die letzten Monate nicht befolgt, aber es war mein Eindruck, dass dies immer noch im 0x-Entwurf war und daher noch nicht wirklich erforderlich war (oder sein wird, wenn eine Bibliothek nur '03 'implementiert). –

+3

Sutter sagt in einem Kommentar zu diesem Beitrag, "aktuelle ISO C++ erfordert & str [0], um einen Zeiger auf zusammenhängende String-Daten zu husten (aber nicht unbedingt null-terminiert!)," Was die Verwendung des OP tatsächlich korrigieren würde. Ich kann jedoch nichts finden, was das im Standard sagt (zumindest nicht in 21.3.4 lib.string.access). –

+0

Ich denke, das könnte richtig sein; Der std-Fehler 530 sagt, dass operator [] zusammenhängend ist, aber die Iterator-Schnittstelle nicht garantiert ist, und zitiert 23.4.4. Ich grabe meinen Standard aus, um nachzusehen. –

6

Technisch, nein, seit std::string ist nicht erforderlich, seinen Inhalt zusammenhängend im Speicher zu speichern.

Allerdings, in fast allen Implementierungen (jede Implementierung von denen ich bewusst bin), die Inhalte werden zusammenhängend gespeichert und dies würde "funktionieren."

+0

Können Sie einige Implementierungen identifizieren, in denen es nicht funktionieren würde? –

+2

Nein. Aber Sie könnten eine solche Implementierung machen, wenn Sie wollten. –

+0

@Neil: Haben Sie eine Verbindung/Referenz zu diesem TC? –

2

Leser sollten beachten, dass diese Frage im Jahr 2009 gestellt wurde, als der C++ 03 Standard die aktuelle Publikation war. Diese Antwort basiert auf der Version des Standards, in der std::string s nicht garantiert, um zusammenhängende Speicher zu verwenden. Da diese Frage nicht im Zusammenhang mit einer bestimmten Plattform (wie gcc) gestellt wurde, mache ich keine Annahmen über die Plattform von OP - insbesondere Wetter oder nicht verwendet es für die string.

Legal? Vielleicht, vielleicht nicht. Sicher? Wahrscheinlich, aber vielleicht auch nicht. Guter Code? Nun, lass uns nicht dorthin gehen ...

Warum nicht einfach tun:

std::string s = str; 

... oder:

std::string s(str); 

... oder:

std::string s; 
std::copy(&str[0], &str[strLen], std::back_inserter(s)); 

... oder:

std::string s; 
s.assign(str, strLen); 

?

+0

oder s.assign (str, strLen); –

+0

gut, aktualisiert w/zuweisen –

+1

'std :: string s (str, strLen);' (Kürzeste Form identisch, im Falle von eingebetteten Nullen oder fehlenden Null Terminierung, auf das ursprüngliche Verhalten von der Frage.) –

0

Dies ist in der Regel nicht sicher, unabhängig davon, ob die interne Zeichenfolge Sequenz im Speicher kontinuierlich gespeichert ist oder nicht. Es gibt möglicherweise viele andere Implementierungsdetails, die sich darauf beziehen, wie die gesteuerte Sequenz neben der Kontinuität durch das Objekt std::string gespeichert wird.

Ein echtes praktisches Problem damit könnte das Folgende sein. Die gesteuerte Sequenz von std::string muss nicht als Null-terminierte Zeichenfolge 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 der Methode c_str() vereinfacht: Geben Sie einfach einen Zeiger auf den internen Puffer zurück erledigt.

Der Code, den Sie in Ihrer Frage angegeben haben, unternimmt keine Anstrengungen, die Daten, die er in den internen Puffer kopiert hat, auf Null zu setzen. Möglicherweise weiß es einfach nicht, ob eine Null-Terminierung in dieser Implementierung von std::string notwendig ist. Es ist sehr wahrscheinlich, dass es darauf ankommt, dass der interne Puffer nach dem Aufruf von resize mit Nullen gefüllt wird, so dass das zusätzliche Zeichen, das für den Nullabschluss durch die Implementierung zugewiesen wird, bequem auf Null voreingestellt ist. All dies ist Implementierungsdetail, was bedeutet, dass diese Technik von einigen eher fragilen Annahmen abhängt.

Mit anderen Worten, in einigen Implementierungen müssten Sie wahrscheinlich strcpy, nicht memcpy verwenden, um die Daten so in die kontrollierte Sequenz zu zwingen. In einigen anderen Implementierungen müssten Sie memcpy und nicht strcpy verwenden.

+1

Nach dem Aufruf von 'resize' können Sie ziemlich sicher sein, dass die interne Zeichenkette wie von der Implementierung benötigt null-terminiert ist oder nicht. Nach einem Aufruf von 'resize' nach allem müssen Sie eine gültige Zeichenfolge von n Zeichen haben (mit Nullzeichen nach Bedarf aufgefüllt). - Es zeigt jedoch ein mangelndes Verständnis für die Klasse "std :: string": memcpy wird entweder aus Unwissenheit oder als fehlgeleiteter Leistungsversuch verwendet (aufgrund des Aufrufs "Größe ändern" ordnet der Code dem Puffer Werte zu) zweimal). – UncleBens

+0

@UncleBens: Ich verstehe deinen ersten Satz nicht. In jedem Fall, ja, der Sprachstandard garantiert, dass der größenerhöhende 'resize' Aufruf die Zeichenkette mit Nullen auffüllt. Der Standard garantiert jedoch das Auffüllen nur bis zur angeforderten Größe (in diesem Fall "strLength"), aber es gibt keine Garantie im Standard für dieses zusätzliche Zeichen, wenn die Implementierung ein solches zuweist. – AnT

0

Der Code könnte funktionieren, aber eher durch Zufall als durch Urteil, er macht Annahmen über die Implementierung, die nicht garantiert sind. Ich schlage vor, die Bestimmung der Gültigkeit des Codes keine Rolle spielt, während es eine sinnlose über Komplikation ist, die leicht reduziert ist nur:

std::string s(str) ; 

oder wenn die Zuordnung zu einem bestehenden std :: string-Objekt, nur:

s = str ; 

und dann lassen Sie std :: string selbst bestimmen, wie Sie das Ergebnis erzielen. Wenn Sie auf diese Art von Unsinn zurückgreifen, dann können Sie genauso gut nicht std :: string verwenden und sich daran halten, da Sie alle mit C-Strings verbundenen Gefahren wieder einführen.

+0

Ich kann nicht wirklich sicher sein, dass die zugewiesene Zeichenfolge null terminiert ist. Also das Beste, was ich tun könnte, ist wahrscheinlich am Assign (ptr, ptrLength); Das ist immer noch eine Verbesserung, denke ich. – paxos1977

+0

Verwenden Sie die Konstruktorform: 'std :: string s (str, strLen);' – GManNickG

6

Es ist sicher zu bedienen. Ich denke die meisten Antworten waren einmal korrekt, aber der Standard hat sich geändert. Zitiert von C++ 11-Standard, basic_string allgemeinen Anforderungen [string.require], 21.4.1.5, sagt:

Die kohleartige Objekte in einem basic_string Objekt werden zusammenhängend gespeichert werden.Das heißt, für jede basic_string Objekt s, die Identität & * (s.begin() + n) == & * s.begin() + n gilt für alle Werte von n zu halten, so daß 0 < = n < s .Größe().

Ein bisschen davor heißt es, dass alle Iteratoren Random-Access-Iteratoren sind. Beide Bits unterstützen die Verwendung Ihrer Frage. (Darüber hinaus verwendet Stroustrup es offenbar in seinem neuesten Buch;))

Es ist nicht unwahrscheinlich, dass diese Änderung in C++ 11 vorgenommen wurde. Ich erinnere mich, dass die gleiche Garantie dann für den Vektor hinzugefügt wurde, der auch den sehr nützlichen Zeiger mit dieser Version bekam.

Hoffe, dass hilft.

+2

Die Frage war pre-C++ 11 (es ist als solches getaggt). Sie haben Recht, C++ 11 hat es offiziell abgesichert, dies zu tun. – paxos1977

Verwandte Themen