Hoe kan ik een std::string
omzetten naar een char*
of een const char*
?
Als je alleen een std::string
wilt doorgeven aan een functie die const char*
nodig heeft, kun je
std::string str;
const char * c = str.c_str();
Als je een beschrijfbare kopie wilt krijgen, zoals char *
, dan kun je dat hiermee doen:
std::string str;
char * writable = new char[str.size() + 1];
std::copy(str.begin(), str.end(), writable);
writable[str.size()] = '\0'; // don't forget the terminating 0
// don't forget to free the string after finished using it
delete[] writable;
Edit: Merk op dat het bovenstaande niet exception safe is. Als er iets tussen de new
aanroep en de delete
aanroep komt, zal je geheugen lekken, omdat niets automatisch delete
voor je zal aanroepen. Er zijn twee directe manieren om dit op te lossen.
boost::scoped_array
zal het geheugen voor je verwijderen als je buiten de scope gaat:
std::string str;
boost::scoped_array<char> writable(new char[str.size() + 1]);
std::copy(str.begin(), str.end(), writable.get());
writable[str.size()] = '\0'; // don't forget the terminating 0
// get the char* using writable.get()
// memory is automatically freed if the smart pointer goes
// out of scope
Dit is de standaard manier (vereist geen externe bibliotheek). U gebruikt std::vector
, die het geheugen volledig voor u beheert.
std::string str;
std::vector<char> writable(str.begin(), str.end());
writable.push_back('\0');
// get the char* using &writable[0] or &*writable.begin()
Gegeven zeg...
std::string x = "hello";
const char* p_c_str = x.c_str();
const char* p_data = x.data();
char* p_writable_data = x.data(); // for non-const x from C++17
const char* p_x0 = &x[0];
char* p_x0_rw = &x[0]; // compiles iff x is not const...
Alle bovenstaande pointers zullen dezelfde waarde bevatten - het adres van het eerste karakter in de buffer. Zelfs een lege string heeft een "eerste karakter in de buffer", omdat C++11 garandeert om altijd een extra NUL/0 terminator karakter te bewaren na de expliciet toegewezen string inhoud (bijv. std::string("this\0that", 9)
zal een buffer hebben die "this\0that0"
bevat).
Gegeven een van de bovenstaande pointers:
char c = p[n]; // valid for n <= x.size()
// i.e. you can safely read the NUL at p[x.size()]
Alleen voor de niet-const
pointer p_writable_data
en van &x[0]
:
p_writable_data[n] = c;
p_x0_rw[n] = c; // valid for n <= x.size() - 1
// i.e. don't overwrite the implementation maintained NUL
Het schrijven van een NUL elders in de string verandert niet de string
's size()
; string
's mogen elk aantal NULs bevatten - ze krijgen geen speciale behandeling door std::string
(hetzelfde in C++03).
In C++03, waren de dingen aanzienlijk ingewikkelder (belangrijkste verschillen highlighted):
x.data()
const char*
terug aan de string's interne buffer die volgens de standaard niet hoefde te eindigen met een NUL (d.w.z. kon zijn ['h', 'e', 'l', 'l', 'o']
gevolgd door ongeïnitialiseerde of garbage waarden, met toevallige toegang daartoe met ongedefinieerd gedrag).x.size()
tekens zijn veilig om te lezen, d.w.z. x[0]
tot en met x[x.size() - 1]
&x[0]
f(const char* p, size_t n) { if (n == 0) return; ...whatever... }
moet je f(&x[0], x.size());
niet aanroepen als x.empty()
- gebruik gewoon f(x.data(), ...)
.x.data()
maar:const
x
levert dit een non-const
char*
pointer op; je kunt string-inhoud overschrijvenx.c_str()
const char*
terug naar een ASCIIZ (NUL-terminated) representatie van de waarde (d.w.z. ['h', 'e', 'l', 'o', '\0']).x.data()
en &x[0]
x.size()
+ 1 karakters zijn veilig om te lezen.string
member functie aanroept die de string
wijzigt of verdere capaciteit reserveert, worden alle pointer waarden die eerder zijn geretourneerd door een van de bovenstaande methoden ongeldig gemaakt. Je kunt deze methodes opnieuw gebruiken om een andere pointer te krijgen. (De regels zijn hetzelfde als voor iterators in string
s).
Zie ook Hoe een karakter pointer geldig te krijgen zelfs nadat x
de scope verlaat of verder is gewijzigd hieronder....
Dus, wat is beter om te gebruiken?Vanaf C++11, gebruik .c_str()
voor ASCIIZ data, en .data()
voor "binary" data (hieronder verder uitgelegd).
In C++03, gebruik .c_str()
tenzij u zeker weet dat .data()
adequaat is, en geef de voorkeur aan .data()
boven &x[0]
omdat het veilig is voor lege strings....
...probeer het programma voldoende te begrijpen om data()
te gebruiken wanneer dat nodig is, anders maak je waarschijnlijk andere fouten...
Het ASCII NUL '\0' karakter dat gegarandeerd wordt door .c_str()
wordt door veel functies gebruikt als een sentinel waarde die het einde van relevante en veilig-toegankelijke data aangeeft. Dit geldt zowel voor C++-only functies zoals bijvoorbeeld fstream::fstream(const char* filename, ...)
als voor gedeelde-met-C functies zoals strchr()
, en printf()
.
Gegeven C++03's .c_str()
's garanties over de geretourneerde buffer zijn een super-verzameling van .data()
's, kun je altijd veilig .c_str()
gebruiken, maar mensen doen dat soms niet omdat:
.data()
communiceert aan andere programmeurs die de broncode lezen dat de data geen ASCIIZ is (eerder, je'gebruikt de string om een blok data op te slaan (die soms'niet eens echt tekstueel is)), of dat je'`het doorgeeft aan een andere functie die het behandelt als een blok "binaire" data. Dit kan een cruciaal inzicht zijn om ervoor te zorgen dat andere programmeurs'codewijzigingen de gegevens goed blijven behandelen.string
implementatie wat extra geheugen allocatie en/of data copying moet doen om de NUL terminated buffer voor te bereiden
Als een verdere hint, als de parameters van een functie de (const
) char*
vereisen maar er niet op staan om x.size()
te krijgen, dan heeft de functie waarschijnlijk een ASCIIZ invoer nodig, dus .c_str()
is een goede keuze (de functie moet op de een of andere manier weten waar de tekst eindigt, dus als het geen aparte parameter is kan het alleen een conventie zijn zoals een lengte-prefix of sentinel of een vaste verwachte lengte).
Hoe krijg je een karakter pointer geldig zelfs nadat x
de scope verlaat of verder is gewijzigdJe moet de inhoud van de string
x
copy naar een nieuw geheugengebied buiten x
. Deze externe buffer kan op vele plaatsen zijn zoals een andere string
of character array variabele, het kan wel of niet een andere levensduur hebben dan x
omdat het in een andere scope is (b.v. namespace, global, static, heap, shared memory, memory mapped file).
Om de tekst van std::string x
te kopiëren naar een onafhankelijke karakter array:
// USING ANOTHER STRING - AUTO MEMORY MANAGEMENT, EXCEPTION SAFE
std::string old_x = x;
// - old_x will not be affected by subsequent modifications to x...
// - you can use `&old_x[0]` to get a writable char* to old_x's textual content
// - you can use resize() to reduce/expand the string
// - resizing isn't possible from within a function passed only the char* address
std::string old_x = x.c_str(); // old_x will terminate early if x embeds NUL
// Copies ASCIIZ data but could be less efficient as it needs to scan memory to
// find the NUL terminator indicating string length before allocating that amount
// of memory to copy into, or more efficient if it ends up allocating/copying a
// lot less content.
// Example, x == "ab\0cd" -> old_x == "ab".
// USING A VECTOR OF CHAR - AUTO, EXCEPTION SAFE, HINTS AT BINARY CONTENT, GUARANTEED CONTIGUOUS EVEN IN C++03
std::vector<char> old_x(x.data(), x.data() + x.size()); // without the NUL
std::vector<char> old_x(x.c_str(), x.c_str() + x.size() + 1); // with the NUL
// USING STACK WHERE MAXIMUM SIZE OF x IS KNOWN TO BE COMPILE-TIME CONSTANT "N"
// (a bit dangerous, as "known" things are sometimes wrong and often become wrong)
char y[N + 1];
strcpy(y, x.c_str());
// USING STACK WHERE UNEXPECTEDLY LONG x IS TRUNCATED (e.g. Hello\0->Hel\0)
char y[N + 1];
strncpy(y, x.c_str(), N); // copy at most N, zero-padding if shorter
y[N] = '\0'; // ensure NUL terminated
// USING THE STACK TO HANDLE x OF UNKNOWN (BUT SANE) LENGTH
char* y = alloca(x.size() + 1);
strcpy(y, x.c_str());
// USING THE STACK TO HANDLE x OF UNKNOWN LENGTH (NON-STANDARD GCC EXTENSION)
char y[x.size() + 1];
strcpy(y, x.c_str());
// USING new/delete HEAP MEMORY, MANUAL DEALLOC, NO INHERENT EXCEPTION SAFETY
char* y = new char[x.size() + 1];
strcpy(y, x.c_str());
// or as a one-liner: char* y = strcpy(new char[x.size() + 1], x.c_str());
// use y...
delete[] y; // make sure no break, return, throw or branching bypasses this
// USING new/delete HEAP MEMORY, SMART POINTER DEALLOCATION, EXCEPTION SAFE
// see boost shared_array usage in Johannes Schaub's answer
// USING malloc/free HEAP MEMORY, MANUAL DEALLOC, NO INHERENT EXCEPTION SAFETY
char* y = strdup(x.c_str());
// use y...
free(y);
char*
of const char*
gegenereerd uit een string
te willenHierboven heb je dus gezien hoe je een (const
) char*
krijgt, en hoe je een kopie van de tekst maakt, onafhankelijk van de originele string
, maar wat kun je er mee doen? Een willekeurige greep uit de voorbeelden...
string
's tekst, zoals in printf("x is '%s'", x.c_str());
x
's tekst naar een buffer gespecificeerd door uw functie's aanroeper (bijv. strncpy(callers_buffer, callers_buffer_size, x.c_str())
), of vluchtig geheugen gebruikt voor apparaat I/O (bijv. for (const char* p = x.c_str(); *p; ++p) *p_device = *p;
)x
's tekst toe aan een character array die al ASCIIZ tekst bevat (b.v. strcat(other_buffer, x.c_str())
) - wees voorzichtig dat je de buffer niet overschrijdt (in veel situaties moet je strncat
gebruiken)const char*
of char*
terug vanuit een functie (misschien om historische redenen - client's gebruiken je bestaande API - of voor C compatibiliteit wil je geen std::string
teruggeven, maar wil je wel je string
's data ergens naar toe kopiëren voor de aanroeper)string
variabele waarnaar die pointer wees, de scope heeft verlatenstd::string
implementaties (b.v. STLport en compiler-native) kunnen data doorgeven als ASCIIZ om conflicten te vermijdenGebruik de .c_str()
methode voor const char *
.
Je kunt &mystring[0]
gebruiken om een char *
pointer te krijgen, maar er zijn een paar gotcha's: je zult'niet noodzakelijkerwijs een string met nul beëindigingen krijgen, en je zult'niet in staat zijn om de string's grootte te veranderen. Je moet vooral oppassen geen tekens toe te voegen voorbij het einde van de string of je'krijgt een buffer overrun (en waarschijnlijke crash).
Er was geen garantie dat alle karakters deel zouden uitmaken van dezelfde aaneengesloten buffer tot C++11, maar in de praktijk werkten alle bekende implementaties van std::string
toch op die manier; zie Does "&s[0]" point to contiguous characters in a std::string?.
Merk op dat veel string
lid functies de interne buffer opnieuw zullen toewijzen en alle pointers die je misschien hebt opgeslagen ongeldig zullen maken. Het beste is om ze direct te gebruiken en dan weg te gooien.