Ako môžem konvertovať std::string
na char*
alebo const char*
?
Ak chcete len odovzdať std::string
funkcii, ktorá potrebuje const char*
, môžete použiť
std::string str;
const char * c = str.c_str();
Ak chcete získať zapisovateľnú kópiu, ako je char *
, môžete to urobiť pomocou tohto:
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: Všimnite si, že vyššie uvedený postup nie je bezpečný pre výnimky. Ak čokoľvek medzi volaním new
a volaním delete
vyhodí, dôjde k úniku pamäte, pretože nič za vás automaticky nezavolá delete
. Existujú dva okamžité spôsoby, ako to vyriešiť.
boost::scoped_array
vám pri výstupe z rozsahu vymaže pamäť:
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
Toto je štandardný spôsob (nevyžaduje žiadnu externú knižnicu). Používate std::vector
, ktorý kompletne spravuje pamäť za vás.
std::string str;
std::vector<char> writable(str.begin(), str.end());
writable.push_back('\0');
// get the char* using &writable[0] or &*writable.begin()
Vzhľadom na to, že...
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...
Všetky uvedené ukazovatele budú mať tú istú hodnotu - adresu prvého znaku v bufferi. Dokonca aj prázdny reťazec má "prvý znak vo vyrovnávacej pamäti", pretože C++11 zaručuje, že za explicitne priradeným obsahom reťazca vždy ponechá dodatočný ukončovací znak NUL/0 (napr. std::string("this\0that", 9)
bude mať vyrovnávaciu pamäť s adresou "this\0that\0"
).
Vzhľadom na ktorýkoľvek z vyššie uvedených ukazovateľov:
char c = p[n]; // valid for n <= x.size()
// i.e. you can safely read the NUL at p[x.size()]
Len pre nekonštantný
ukazovateľ p_writable_data
a z &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
Zápis NUL na iné miesto v reťazci nezmení reťazec
's size()
; reťazec
's môže obsahovať ľubovoľný počet NUL - `std::string`` s nimi nezaobchádza nijako špeciálne (rovnako v C++03).
V C++03 bolo všetko podstatne komplikovanejšie (kľúčové rozdiely sú zvýraznené***):
x.data()
const char*
do interného buffera reťazca, ktorý podľa normy nemusel byť ukončený NUL (t. j. mohol byť ['h', 'e', 'l', 'l', 'o']
nasledovaný neinicializovanými alebo odpadovými hodnotami, pričom náhodné prístupy k nim mali nedefinované správanie).x.size()
sú bezpečné na čítanie, t. j. x[0]
až x[x.size() - 1]
&x[0]
f(const char* p, size_t n) { if (n == 0) return; ...whatever... }
nesmiete volať f(&x[0], x.size());
keď x.empty()
- stačí použiť f(x.data(), ...)
.x.data()
, ale:konštantné
x
to dáva ne-konštantný
znak*
ukazovateľ; môžete prepísať obsah reťazcax.c_str()
const char*
na ASCIIZ (NUL-koncovku) reprezentáciu hodnoty (t.j. ['h', 'e', 'l', 'l', 'o', '\0']).x.data()
a &x[0]
x.size()
+ 1 znakov je bezpečné čítať.string
, ktorá modifikuje string
alebo rezervuje ďalšiu kapacitu, všetky hodnoty ukazovateľov vrátené predtým niektorou z uvedených metód sú zneplatnené. Tieto metódy môžete použiť znova, aby ste získali iný ukazovateľ. (Pravidlá sú rovnaké ako pre iterátory do string
).
Pozri tiež Ako získať platnosť ukazovateľa na znak aj po tom, čo x
opustí rozsah alebo je ďalej modifikovaný nižšie....
Čo je teda lepšie použiť?Od C++11 používajte .c_str()
pre dáta ASCIIZ a .data()
pre "binárne" dáta (vysvetlené ďalej).
V C++03 používajte .c_str()
, pokiaľ si nie ste istí, že .data()
je adekvátne, a uprednostnite .data()
pred &x[0]
, pretože je'bezpečný pre prázdne reťazce....
...snažte sa pochopiť program natoľko, aby ste mohli použiť data()
, keď je to vhodné, inak sa pravdepodobne dopustíte ďalších chýb...
Znak ASCII NUL '\0' garantovaný funkciou .c_str()
sa používa v mnohých funkciách ako strážna hodnota označujúca koniec relevantných a bezpečne prístupných údajov. To sa týka tak funkcií len pre jazyk C++, ako napríklad fstream::fstream(const char* filename, ...)
, ako aj funkcií zdieľaných s C, ako napríklad strchr()
a printf()
.
Vzhľadom na to, že záruky C++03's .c_str()
's ohľadom na vrátený buffer sú nadmnožinou .data()
's, môžete vždy bezpečne použiť .c_str()
, ale ľudia to niekedy nerobia, pretože:
.data()
oznamujete ostatným programátorom čítajúcim zdrojový kód, že dáta nie sú ASCIIZ (skôr používate reťazec na uloženie bloku dát (ktorý niekedy ani nie je textový)), alebo že ho odovzdávate inej funkcii, ktorá s ním zaobchádza ako s blokom "binárnych" dát. Toto môže byť rozhodujúci poznatok, ktorý zabezpečí, že ďalší programátori' pri zmenách kódu budú s údajmi naďalej správne zaobchádzať.stringu
bude musieť vykonať dodatočnú alokáciu pamäte a/alebo kopírovanie dát, aby pripravila buffer ukončený NUL
Ako ďalšia rada, ak parametre funkcie'vyžadujú (const
) char*
, ale netrvajú na získaní x.size()
, funkcia pravdepodobne potrebuje ASCIIZ vstup, takže .c_str()
je dobrou voľbou (funkcia musí nejako vedieť, kde text končí, takže ak to'nie je samostatný parameter, môže to byť len konvencia ako length-prefix alebo sentinel alebo nejaká pevná očakávaná dĺžka).
Ako získať ukazovateľ na znak platný aj po tom, čo x
opustí rozsah alebo je ďalej modifikovanýBudete musieť kopírovať obsah reťazca
x
do novej oblasti pamäte mimo x
. Táto externá vyrovnávacia pamäť môže byť na mnohých miestach, napríklad v inom stringu
alebo v premennej znakového poľa, môže, ale nemusí mať inú životnosť ako x
z dôvodu, že je v inom rozsahu (napr. v mennom priestore, globálnom, statickom, na halde, v zdieľanej pamäti, v súbore mapovanom na pamäť).
Kopírovanie textu z premennej std::string x
do nezávislého znakového poľa:
// 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);
string
vygeneroval znak char*
alebo const char*
Takže vyššie ste videli, ako získať (const
) char*
a ako vytvoriť kópiu textu nezávislú od pôvodného stringu
, ale čo s ním môžete robiť? Náhodný výber príkladov...
stringu
'textu, ako v printf("x is '%s'", x.c_str());
x
'do vyrovnávacej pamäte určenej volajúcim funkcie (napr. strncpy(callers_buffer, callers_buffer_size, x.c_str())
) alebo do prchavej pamäte používanej na vstupno-výstupné operácie zariadenia (napr. for (const char* p = x.c_str(); *p; ++p) *p_device = *p;
)x
'k znakovému poľu, ktoré už obsahuje nejaký text ASCIIZ (napr. strcat(other_buffer, x.c_str())
) - dávajte pozor, aby ste neprekročili buffer (v mnohých situáciách možno budete musieť použiť strncat
)const char*
alebo char*
(možno z historických dôvodov - klient'používa vaše existujúce API - alebo kvôli kompatibilite s C nechcete vrátiť std::string
, ale chcete pre volajúceho niekde skopírovať dáta vášho `stringu')string
, na ktorú tento ukazovateľ ukazoval, opustila rozsahstd::string
(napr. STLport a compiler-native) môžu odovzdávať údaje ako ASCIIZ, aby sa predišlo konfliktomPoužite metódu .c_str()
pre const char *
.
Môžete použiť &mystring[0]
na získanie ukazovateľa char *
, ale je tu niekoľko háčikov: nemusíte získať reťazec ukončený nulou a nebudete môcť zmeniť veľkosť reťazca. Zvlášť si musíte dávať pozor, aby ste nepridávali znaky za koniec reťazca, inak dôjde k prekročeniu vyrovnávacej pamäte (a pravdepodobne k pádu).
Do C++11 neexistovala záruka, že všetky znaky budú súčasťou toho istého súvislého buffera, ale v praxi tak aj tak fungovali všetky známe implementácie std::string
; pozri Ukazuje "&s[0]" na súvislé znaky v std::string?.
Všimnite si, že mnohé členské funkcie string
prerozdelia vnútornú vyrovnávaciu pamäť a zrušia všetky uložené ukazovatele. Najlepšie je ich okamžite použiť a potom zahodiť.