Hvordan kan jeg konvertere en std::string
til en char*
eller en const char*
?
Hvis du bare vil sende en std::string
til en funktion, der har brug for const char*
, kan du bruge
std::string str;
const char * c = str.c_str();
Hvis du ønsker at få en skrivbar kopi, som char *
, kan du gøre det med dette:
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: Bemærk, at ovenstående ikke er exception safe. Hvis noget mellem new
-opkaldet og delete
-opkaldet kaster, vil du lække hukommelse, da intet vil kalde delete
for dig automatisk. Der er to umiddelbare måder at løse dette på.
[boost::scoped_array
] (http://www.boost.org/doc/libs/release/libs/smart_ptr/scoped_array.htm) sletter hukommelsen for dig, når den går ud af scope:
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
Dette er standardmetoden (kræver ikke noget eksternt bibliotek). Du bruger std::vector
, som helt styrer hukommelsen for dig.
std::string str;
std::vector<char> writable(str.begin(), str.end());
writable.push_back('\0');
// get the char* using &writable[0] or &*writable.begin()
Givet siger...
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 ovenstående pointere vil indeholde den samme værdi - adressen på det første tegn i bufferen. Selv en tom streng har et "første tegn i bufferen", fordi C++11 garanterer altid at beholde et ekstra NUL/0-terminatortegn efter det eksplicit tildelte strengindhold (f.eks. vil std::string("this\0that", 9)
have en buffer, der indeholder "this\0that\0"
).
Givet en af de ovennævnte pointere:
char c = p[n]; // valid for n <= x.size()
// i.e. you can safely read the NUL at p[x.size()]
Kun for den ikke-konstante pointer p_writable_data
og fra &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
At skrive en NUL et andet sted i strengen ændrer ikke strengen
's size()
; strenge
's må indeholde et hvilket som helst antal NUL'er - de får ingen særbehandling af std::string
(det samme i C++03).
I C++03 var tingene betydeligt mere komplicerede (de vigtigste forskelle highlighted):
x.data()
const char*
til stringens interne buffer som ifølge standarden ikke skulle afsluttes med et NUL (dvs. kunne være ['h', 'e', 'l', 'l', 'l', 'o']
efterfulgt af uinitialiserede værdier eller garbage-værdier, og utilsigtet adgang hertil havde udefineret adfærd).x.size()
-tegn er sikre at læse, dvs. x[0]
til og med x[x.size() - 1]
.&x[0]
f(const char* p, size_t n) { if (n == 0) return; ...whatever... }
skal du ikke kalde f(&x[0], x.size()));
når x.empty()
- brug bare f(x.data(), ...)
.x.data()
, men:const
x
giver dette en non-const
char*
pointer; du kan overskrive strengindholdetx.c_str()
const char*
til en ASCIIZ (NUL-termineret) repræsentation af værdien (dvs. ['h', 'e', 'l', 'l', 'l', 'o', '\0']).x.data()
og &x[0]
x.size()
+ 1 tegn er sikre at læse.string
-medlemsfunktion, der ændrer strengen
eller reserverer yderligere kapacitet, bliver alle pointerværdier, der er returneret på forhånd af en af de ovennævnte metoder, invalideret. Du kan bruge disse metoder igen for at få en anden pointer. (Reglerne er de samme som for iteratorer ind i string
s).
Se også Hvordan man får en tegnpointer gyldig, selv efter at x
forlader scope eller ændres yderligere nedenfor....
Så hvad er bedre at bruge?Fra C++11 skal du bruge .c_str()
til ASCIIZ-data og .data()
til "binære" data (forklares yderligere nedenfor).
I C++03 skal du bruge .c_str()
, medmindre du er sikker på, at .data()
er passende, og foretrækker .data()
frem for &x[0]
, da det er sikkert for tomme strenge....
...prøv at forstå programmet nok til at bruge data()
når det er hensigtsmæssigt, ellers vil du sandsynligvis begå andre fejl...
ASCII-tegnet NUL '\0', som garanteres af .c_str()
, bruges af mange funktioner som en sentinel-værdi, der angiver slutningen af relevante og sikre data, som man kan få adgang til. Dette gælder både for C++-funktioner som f.eks. fstream::fstream(const char* filnavn, ...)
og funktioner, der deles med C-funktioner som strchr()
og printf()
.
Da C++03's .c_str()
's garantier om den returnerede buffer er en overordnet mængde af .data()
's, kan du altid roligt bruge .c_str()
, men folk gør det nogle gange ikke, fordi:
.data()
, meddeler du andre programmører, der læser kildekoden, at dataene ikke er ASCIIZ (du bruger strengen til at gemme en blok data (som nogle gange ikke engang er en rigtig tekst)), eller at du sender den til en anden funktion, der behandler den som en blok af "binære" data. Dette kan være en afgørende indsigt for at sikre, at andre programmører fortsat håndterer dataene korrekt ved kodeændringer.string
-implementering skal foretage en ekstra hukommelsesallokering og/eller datakopiering for at forberede den NUL-terminerede buffer.
Som et yderligere tip, hvis en funktions parametre kræver (const
) char*
, men ikke insisterer på at få x.size()
, har funktionen sandsynligvis brug for et ASCIIZ-input, så .c_str()
er et godt valg (funktionen skal vide, hvor teksten slutter på en eller anden måde, så hvis det ikke er en separat parameter, kan det kun være en konvention som et længde-prefix eller en sentinel eller en fast forventet længde).
Hvordan får man en tegnpointer gyldig, selv efter at x
forlader scope eller ændres yderligereDu skal kopiere indholdet af strengen
x
til et nyt hukommelsesområde uden for x
. Denne eksterne buffer kan være mange steder, f.eks. i en anden string
eller i en anden variabel i et karakterarray, og den kan have eller ikke have en anden levetid end x
, fordi den befinder sig i et andet scope (f.eks. namespace, global, statisk, heap, shared memory, memory mapped file).
Sådan kopieres teksten fra std::string x
til et uafhængigt karakterarray:
// 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*
eller const char*
genereret fra en string
Så ovenfor har du set hvordan man får en (const
) char*
, og hvordan man laver en kopi af teksten uafhængigt af den oprindelige string
, men hvad kan man gøre med den? Et tilfældigt udvalg af eksempler...
string
's tekst, som i printf("x is '%s'", x.c_str());
x
's tekst til en buffer, der er angivet af din funktions opkalder (f.eks. strncpy(callers_buffer, callers_buffer_size, x.c_str())
), eller flygtig hukommelse, der bruges til device I/O (f.eks. for (const char* p = x.c_str(); *p; ++p) *p_device = *p;
)x
's tekst til et tegn array, der allerede indeholder ASCIIZ tekst (f.eks. strcat(other_buffer, x.c_str())
) - vær forsigtig med ikke at overfylde bufferen (i mange situationer kan du have brug for at bruge strncat
)const char*
eller char*
fra en funktion (måske af historiske årsager - klienten bruger din eksisterende API - eller af hensyn til C-kompatibilitet ønsker du ikke at returnere en std::string
, men ønsker at kopiere din string
et sted hen til den der kalder)string
-variabel, som pointeren pegede på, har forladt scopestd::string
-implementeringer (f.eks. STLport og compiler-native) kan videregive data som ASCIIZ for at undgå konflikter.Brug metoden .c_str()
for const char *
.
Du kan bruge &mystring[0]
til at få en char *
pointer, men der er et par problemer: du får ikke nødvendigvis en nul-termineret streng, og du kan ikke ændre strengen i størrelse. Du skal især være forsigtig med ikke at tilføje tegn efter strengen, ellers får du en bufferoverløb (og sandsynligvis et nedbrud).
Der var ingen garanti for, at alle tegn ville være en del af den samme sammenhængende buffer indtil C++11, men i praksis fungerede alle kendte implementationer af std::string
alligevel på den måde; se [Does "&s[0]" point to contiguous characters in a std::string?] (https://stackoverflow.com/questions/1986966/does-s0-point-to-contiguous-characters-in-a-stdstring).
Bemærk, at mange string
-medlemsfunktioner vil genallokere den interne buffer og ugyldiggøre eventuelle pointere, du måtte have gemt. Det er bedst at bruge dem med det samme og derefter kassere dem.