12 Stimmen

Worauf ist beim Konvertieren eines std:: string in ein char* für eine C-Funktion zu achten?

Ich habe viele Beiträge gelesen, die die Frage stellen, wie man ein C++ std::string oder const std::string& in ein char* konvertiert, um es an eine C-Funktion zu übergeben, und es scheint, dass es einige Fallstricke gibt, die man dabei beachten muss. Man muss vorsichtig sein, dass der String zusammenhängend ist und viele andere Dinge. Der Punkt ist, dass ich nie wirklich alle Punkte verstanden habe, auf die man achten muss und warum?

Ich frage mich, ob jemand die Fallstricke und Nachteile zusammenfassen könnte, die bei der Konvertierung eines std::string in ein char* auftreten, das an eine C-Funktion übergeben werden muss?

Das gilt, wenn der std::string eine const Referenz ist und wann es sich nur um eine nicht-const Referenz handelt, und wann die C-Funktion den char* verändern wird und wann nicht.

11voto

James Kanze Punkte 146902

Zunächst ändert es nichts, ob es sich um eine const-Referenz oder -Wert handelt.

Sie müssen dann berücksichtigen, was die Funktion erwartet. Es gibt verschiedene Dinge, die eine Funktion mit einem char* oder einem char const* tun kann - die ursprünglichen Versionen von memcpy verwendeten beispielsweise diese Typen, und es ist möglich, dass solcher Code noch existiert. Hoffentlich ist das selten, und im Folgenden werde ich davon ausgehen, dass die char* in der C-Funktion auf mit '\0' terminierte Zeichenfolgen verweisen.

Wenn die C-Funktion ein char const* erwartet, können Sie ihr die Ergebnisse von std::string::c_str() übergeben. Wenn sie ein char* erwartet, hängt es davon ab. Wenn sie ein char* einfach deshalb erwartet, weil sie aus der Zeit vor const stammt und tatsächlich nichts ändert, ist std::string::c_str() gefolgt von einem const_cast angemessen. Wenn die C-Funktion den char* als Ausgabeparameter verwendet, wird es jedoch schwieriger. Ich ziehe es persönlich vor, ein char[]-Puffer zu deklarieren, diesen zu übergeben und dann die Ergebnisse in std::string umzuwandeln, aber alle bekannten Implementierungen von std::string verwenden einen zusammenhängenden Puffer, und die nächste Version des Standards wird dies erfordern. Daher kann es auch verwendet werden, die std::string zuerst korrekt zu dimensionieren (Verwendung von std::string::resize(), dann Übergeben von &s[0] und anschließend Neudimensionierung der Zeichenfolge auf die resultierende Länge (ermittelt mit strlen(s.c_str()), wenn nötig).

Schließlich (aber dies betrifft auch C-Programme, die char[] verwenden) müssen Sie Lebensdauerfragen berücksichtigen. Die meisten Funktionen, die char* oder char const* verwenden, nutzen einfach den Zeiger und vergessen ihn, aber wenn die Funktion den Zeiger irgendwo speichert, um ihn später zu verwenden, muss das Zeichenfolgenobjekt mindestens so lange leben, und seine Größe darf während dieses Zeitraums nicht geändert werden. (In solchen Fällen ziehe ich es erneut vor, ein char[] zu verwenden.)

6voto

Konrad Rudolph Punkte 503837

Grundsätzlich gibt es drei wichtige Punkte:

  • Laut dem immer noch aktuellen Standard garantiert std::string eigentlich nicht die Verwendung von zusammenhängendem Speicher (soweit ich weiß, soll sich das jedoch ändern). Tatsächlich verwenden jedoch alle aktuellen Implementierungen wahrscheinlich trotzdem zusammenhängenden Speicher. Aus diesem Grund kann c_str() (und data()) intern tatsächlich eine Kopie des Strings erstellen...

  • Der von c_str() (und data()) zurückgegebene Zeiger ist nur gültig, solange keine nicht-const Methoden auf dem Originalstring aufgerufen werden. Dies macht seine Verwendung ungeeignet, wenn die C-Funktion den Zeiger festhält (im Gegensatz zu dessen Verwendung nur während des tatsächlichen Funktionsaufrufs).

  • Wenn auch nur irgendeine Chance besteht, dass der String geändert wird, ist es keine gute Idee, die Konstanz des c_str() zu entfernen. Du musst einen Puffer mit einer Kopie des Strings erstellen und diesen in die C-Funktion übergeben. Wenn du einen Puffer erstellst, denke daran, eine Nullterminierung hinzuzufügen.

4voto

Boaz Yaniv Punkte 6166

[Ich würde einen Kommentar hinzufügen, aber ich habe nicht genug Ruf dafür, also entschuldige ich mich für das Hinzufügen einer (weiteren) Antwort.]

Während es wahr ist, dass der aktuelle Standard nicht garantiert, dass der interne Puffer von std::string zusammenhängend ist, scheint es, dass praktisch alle Implementierungen zusammenhängende Puffer verwenden. Darüber hinaus erfordert der neue C++0x-Standard (der kurz vor der Genehmigung durch die ISO steht) zusammenhängende interne Puffer in std::string, und sogar der aktuelle C++03-Standard erfordert die Rückgabe eines zusammenhängenden Puffers, wenn Sie data() oder &str[0] aufrufen (obwohl er nicht unbedingt nullterminiert sein wird). Siehe hier für weitere Details.

Das bedeutet jedoch nicht, dass es sicher ist, in den String zu schreiben, da der Standard Implementierungen nicht dazu zwingt, tatsächlich ihren internen Puffer zurückzugeben, wenn Sie data(), c_str() oder Operator aufrufen, und sie nicht davon abgehalten sind, Optimierungen wie Copy-On-Write zu verwenden, was die Dinge weiter komplizieren könnte (es scheint, dass das neue C++0x Copy-On-Write verbieten wird). Trotzdem, wenn Ihnen maximale Portabilität egal ist, können Sie Ihre Zielimplementierung überprüfen und sehen, was sie tatsächlich intern tut. Soweit ich weiß, gibt Visual C++ 2008/2010 immer den tatsächlichen internen Pufferzeiger zurück und verwendet kein Copy-On-Write (es hat jedoch die Optimierung für kleine Zeichenfolgen, aber das ist wahrscheinlich kein Problem).

2voto

ltjax Punkte 15481

Wenn die C-Funktion den String hinter dem char* nicht ändert, können Sie std::string::c_str() für sowohl const als auch non-const std::string Instanzen verwenden. Idealerweise wäre es ein const char*, aber wenn es das nicht ist (aufgrund einer Legacy-API), können Sie legal ein const_cast verwenden. Aber Sie dürfen den Zeiger von c_str() nur verwenden, solange Sie den String nicht ändern!

Wenn die C-Funktion den char*-String ändert, ist der einzige sichere und portable Weg, std::string zu verwenden, ihn selbst in einen temporären Puffer zu kopieren (zum Beispiel von c_str())! Stellen Sie sicher, dass Sie den temporären Speicher danach freigeben - oder verwenden Sie std::vector, das garantiert kontinuierlichen Speicher hat.

1voto

Tometzky Punkte 20675
  1. std:string kann null Byte speichern. Dies bedeutet, dass sie, wenn sie an eine C-Funktion übergeben wird, vorzeitig abgeschnitten werden kann, da C-Funktionen bei null Byte stoppen. Dies kann Sicherheitsprobleme haben, wenn Sie beispielsweise versuchen, eine C-Funktion zu verwenden, um unerwünschte Zeichen herauszufiltern oder zu escapen.

  2. Ein Ergebnis von std::string::c_str() wird manchmal durch Operationen, die eine Zeichenkette ändern (nicht-const Memberfunktionen), ungültig gemacht. Es wird sehr schwer zu diagnostizierende Bugs ("Heisenbugs") verursachen, wenn Sie dieses Zeiger verwenden, nachdem Sie c_str() zum ersten Mal verwendet haben und dann eine Zeichenkette ändern.

  3. Verwenden Sie niemals const_cast. goto ist weniger problematisch.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X