Jak przekonwertować std:: string na const char * lub char*?
Jak mogę przekonwertować std::string
na char*
lub const char*
?
8 answers
Jeśli chcesz zdać std::string
do funkcji, która potrzebuje const char*
możesz użyć
std::string str;
const char * c = str.c_str();
Jeśli chcesz uzyskać kopię zapisywalną, jak char *
, możesz to zrobić za pomocą tego:
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 : zauważ, że powyższe nie jest bezpieczne. Jeśli coś pomiędzy wywołaniem new
a wywołaniem delete
rzuci, wycieknie pamięć, ponieważ nic nie wywoła delete
automatycznie. Istnieją dwa natychmiastowe sposoby rozwiązania to.
Boost:: scoped_array
boost::scoped_array
usunie pamięć dla ciebie po wyjściu z zakresu:
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
Std::vector
Jest to standardowy sposób (nie wymaga żadnej zewnętrznej biblioteki). Używasz std::vector
, który całkowicie zarządza pamięcią dla Ciebie.
std::string str;
std::vector<char> writable(str.begin(), str.end());
writable.push_back('\0');
// get the char* using &writable[0] or &*writable.begin()
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-06 07:44:34
Powiedziane...
std::string x = "hello";
W tym celu należy skontaktować się z Działem obsługi klienta.]}
Jak uzyskać wskaźnik znakowy, który jest ważny, gdy x
pozostaje w zakresie i nie jest dalej modyfikowany
C++11 upraszcza rzeczy; następujące wszystkie dają dostęp do tego samego wewnętrznego bufora łańcuchów:
const char* p_c_str = x.c_str();
const char* p_data = x.data();
const char* p_x0 = &x[0];
char* p_x0_rw = &x[0]; // compiles iff x is not const...
Wszystkie powyższe wskaźniki będą zawierały tę samą wartość - adres pierwszego znaku w buforze. Nawet pusty ciąg ma "pierwszy znak w buforze", ponieważ C++11 gwarantuje zawsze zachowanie dodatkowego znaku terminatora NUL / 0 po jawnie przypisanej zawartości łańcucha (np. {[6] } będzie miał bufor "this\0that\0"
).
Biorąc pod uwagę którykolwiek z powyższych wskaźników:
char c = p[n]; // valid for n <= x.size()
// i.e. you can safely read the NUL at p[x.size()]
Tylko dla wskaźnika nie-const
z &x[0]
:
p_x0_rw[n] = c; // valid for n <= x.size() - 1
// i.e. don't overwrite the implementation maintained NUL
Zapis NUL gdzie indziej w łańcuchu powoduje Nie zmianę string
's size()
; string
's mogą zawierać dowolną liczbę Nul - nie mają specjalnego treatment by std::string
(to samo w C++03).
W C++03 sprawy były znacznie bardziej skomplikowane (kluczowe różnice wyróżnione):
-
x.data()
- zwraca
const char*
do wewnętrznego bufora łańcuchaktóry nie był wymagany przez Standard do zawarcia z NUL (np. może być['h', 'e', 'l', 'l', 'o']
po których następują wartości niezainicjowane lub śmieci, z przypadkowym dostępem do nich mającym niezdefiniowany zachowanie ).-
x.size()
znaki są bezpieczne do odczytu, tzn.x[0]
przezx[x.size() - 1]
- dla pustych łańcuchów, masz gwarancję jakiegoś wskaźnika non-NULL, do którego można bezpiecznie dodać 0 (hurray!), ale nie powinieneś odwoływać się do tego wskaźnika.
-
- zwraca
-
&x[0]
-
dla pustych łańcuchów ma to nieokreślone zachowanie (21.3.4)
- np. given
f(const char* p, size_t n) { if (n == 0) return; ...whatever... }
you must ' t callf(&x[0], x.size());
whenx.empty()
- just użyjf(x.data(), ...)
.
- np. given
- inaczej, jak na
x.data()
ale:- dla non-
const
x
to daje nie-const
char*
wskaźnik; można nadpisać zawartość ciągu
- dla non-
-
dla pustych łańcuchów ma to nieokreślone zachowanie (21.3.4)
-
x.c_str()
- zwraca
const char*
do zakończonej znakiem NUL reprezentacji wartości (np. ['h', 'e', 'l',' L','O', '\0']). - chociaż kilka implementacji zdecydowało się na to, standard C++03 został sformułowany tak, aby umożliwić ciąg znaków realizacja wolność tworzenia odrębny bufor zakończony NUL w locie , z potencjalnie nie NUL zakończonego bufora "narażonego" przez
x.data()
i&x[0]
-
x.size()
+ 1 znaki są bezpieczne do odczytania. - gwarantowane bezpieczeństwo nawet dla pustych łańcuchów (['\0']).
- zwraca
Konsekwencje dostępu do zewnętrznych wskaźników prawnych
Niezależnie od tego, w jaki sposób uzyskasz wskaźnik, nie możesz uzyskać dostępu do pamięci dalej od wskaźnik niż znaki gwarantowane obecne w opisach powyżej. Próby takie mają niezdefiniowane zachowanie , z bardzo realną szansą na awarie aplikacji i śmieci wyników nawet dla odczytów, a dodatkowo hurtownia danych, uszkodzenie stosu i / lub luki w zabezpieczeniach dla zapisów.
Kiedy te wskaźniki zostaną unieważnione?
Jeśli wywołasz jakąś string
funkcję member, która modyfikuje string
lub rezerwuje dodatkową pojemność, zwracane są wartości wskaźnika wcześniej przez którąkolwiek z powyższych metod są unieważnione . Możesz użyć tych metod ponownie, aby uzyskać kolejny wskaźnik. (Reguły są takie same jak dla iteratorów do string
s).
Zobacz także jak uzyskać poprawny wskaźnik znakowy nawet po opuszczeniu scope przez x
lub jego modyfikacji w dalszej części poniżej....
Więc, który jest lepszy do użycia?
Z C++11, użyj .c_str()
dla danych ASCIIZ, i .data()
dla danych "binarnych" (wyjaśnione poniżej).
W C++03, użyj .c_str()
, chyba że pewne, że .data()
jest odpowiednie, i preferuj .data()
zamiast &x[0]
, ponieważ jest bezpieczny dla pustych łańcuchów....
...spróbuj zrozumieć program na tyle, aby użyć data()
, gdy jest to właściwe, albo prawdopodobnie popełnisz inne błędy...
Znak ASCII NUL '\0' gwarantowany przez .c_str()
jest używany przez wiele funkcji jako wartość sentinel oznaczająca koniec istotnych i bezpiecznych danych dostępu. Dotyczy to zarówno funkcji tylko w C++, takich jak say fstream::fstream(const char* filename, ...)
i funkcje współdzielone z C, takie jak strchr()
i printf()
.
Biorąc pod uwagę Gwarancje C++03 .c_str()
dotyczące zwracanego bufora są super-zbiorem .data()
' S, zawsze możesz bezpiecznie używać .c_str()
, ale ludzie czasami nie, ponieważ:
- użycie
.data()
komunikuje się z innymi programistami czytającymi kod źródłowy, że dane nie są ASCIIZ (raczej używasz ciągu do przechowywania bloku danych (który czasami nie jest nawet tak naprawdę tekstowy)), lub że przekazujesz je do innej funkcji, która traktuje go jako blok "binarnych" danych. Może to być kluczowy wgląd w zapewnienie, że zmiany kodu innych programistów nadal będą prawidłowo obsługiwać dane. - C++03 tylko: istnieje niewielka szansa, że Twoja implementacja
string
będzie musiała wykonać dodatkową alokację pamięci i / lub kopiowanie danych w celu przygotowania bufora zakończonego znakiem NUL
Jako kolejna wskazówka, jeśli parametry funkcji wymagają (const
) char*
ale nie nalegaj na uzyskanie x.size()
, funkcja prawdopodobnie wymaga wejścia ASCIIZ, więc .c_str()
jest dobrym wyborem (funkcja musi wiedzieć, gdzie tekst się kończy, więc jeśli nie jest to oddzielny parametr, może to być tylko konwencja jak prefiks długości lub sentinel lub jakaś stała oczekiwana długość).
Jak uzyskać poprawny wskaźnik znakowy nawet po opuszczeniu zakresu lub jego dalszej modyfikacji
Będziesz musiał Kopiuj Zawartość string
x
do nowego obszaru pamięci poza x
. Ten zewnętrzny bufor może znajdować się w wielu miejscach, takich jak inna zmienna string
lub zmienna tablicy znaków, może, ale nie musi, mieć inny okres życia niż x
ze względu na to, że znajduje się w innym zakresie (np. przestrzeń nazw, globalny, statyczny, sterta, pamięć dzielona, plik mapowany pamięcią).
Aby skopiować tekst z std::string x
do niezależnej tablicy znaków:
// 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);
Inne powody, aby chcieć char*
lub const char*
wygenerowane z string
Więc, powyżej widziałeś, jak uzyskać (const
) char*
, i jak aby Kopia tekstu była niezależna od oryginału string
, ale co można z nim zrobić ? Losowe smattering przykładów...
- dać" C " kod dostępu do tekstu C++
string
, jak wprintf("x is '%s'", x.c_str());
W zależności od tego, która z tych wartości jest większa, można ją zapisać w pamięci podręcznej lub w pamięci podręcznej (np. w pamięci podręcznej).]}
- dołącza tekst
x
do tablicy znaków zawierającej już jakiś tekst ASCIIZ (np.strcat(other_buffer, x.c_str())
) - należy uważać, aby nie w wielu sytuacjach może być konieczne użyciestrncat
) - zwraca
const char*
lubchar*
Z Funkcji (być może ze względów historycznych-Klient korzysta z istniejącego API - lub ze względu na kompatybilność C nie chcesz zwracaćstd::string
, ale chcesz skopiować danestring
gdzieś dla wywołującego)- uważaj, aby nie zwracać wskaźnika, który może być dereferowany przez wywołującego po lokalnej zmiennej
string
, do której ten wskaźnik opuścił zakres - niektóre projekty z współdzielonymi obiektami skompilowane/połączone dla różnych implementacji
std::string
(np. STLport i kompilator natywny) mogą przekazywać dane jako ASCIIZ, aby uniknąć konfliktów
- uważaj, aby nie zwracać wskaźnika, który może być dereferowany przez wywołującego po lokalnej zmiennej
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-11 16:59:05
Użyj metody .c_str()
dla const char *
.
Możesz użyć &mystring[0]
, aby uzyskać wskaźnik char *
, ale jest kilka takich opcji: niekoniecznie otrzymasz zakończony zerem łańcuch i nie będziesz mógł zmienić rozmiaru łańcucha. Musisz szczególnie uważać, aby nie dodawać znaków poza końcem łańcucha, bo dostaniesz przekroczenie bufora (i prawdopodobną awarię).
Nie było gwarancji, że wszystkie znaki będą częścią tego samego sąsiadującego bufora aż do C++11, ale w poćwicz wszystkie znane implementacje std::string
działały w ten sposób; zobacz Czy "&S [0]" wskazuje na sąsiadujące ze sobą znaki w std::string?.
Zauważ, że wiele funkcji Członkowskich string
ponownie przydzieli wewnętrzny bufor i unieważni wszystkie zapisane przez Ciebie wskaźniki. Najlepiej użyć je natychmiast, a następnie wyrzucić.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:23
C++17
C++17 (nadchodzący standard) zmienia streszczenie szablonu basic_string
dodając nieskonsekwentne przeciążenie data()
:
charT* data() noexcept;
Zwraca: wskaźnik p taki, że p + i == &operator dla każdego i w [0, size()].
CharT const *
od std::basic_string<CharT>
std::string const cstr = { "..." };
char const * p = cstr.data(); // or .c_str()
CharT *
od std::basic_string<CharT>
std::string str = { "..." };
char * p = str.data();
C++11
CharT const *
od std::basic_string<CharT>
std::string str = { "..." };
str.c_str();
CharT *
od std::basic_string<CharT>
Począwszy od C++11, standard mówi:
- obiekty podobne do znaków w obiekcie
basic_string
powinny być przechowywane obok siebie. Oznacza to, że dla dowolnegobasic_string
obiektus
tożsamość {[22] } powinna posiadać dla wszystkich wartościn
takie, że0 <= n < s.size()
.
const_reference operator[](size_type pos) const;
reference operator[](size_type pos);
Zwraca:
*(begin() + pos)
ifpos < size()
, w przeciwnym razie odniesienie do obiektu typuCharT
z wartościąCharT()
; wartość odniesienia nie może być modyfikowana.
const charT* c_str() const noexcept;
const charT* data() const noexcept;
Zwraca: wskaźnik p taki, że
p + i == &operator[](i)
dla każdegoi
w[0,size()]
.
Istnieją różne możliwe sposoby uzyskania wskaźnika non-const.
1. Korzystanie z pamięci podręcznej C++11
std::string foo{"text"};
auto p = &*foo.begin();
Pro
- proste i krótkie
- Fast (tylko metoda bez kopiowania)
Cons
- finał
'\0'
nie jest do zmiany / niekoniecznie części pamięci non-const.
2. Użycie std::vector<CharT>
std::string foo{"text"};
std::vector<char> fcv(foo.data(), foo.data()+foo.size()+1u);
auto p = fcv.data();
Pro
- proste
- Automatyczna obsługa pamięci
- dynamiczny
Cons
- wymaga kopii ciągów
3. Użyj std::array<CharT, N>
, jeśli N
jest stałą czasową kompilacji (i wystarczająco małą)
std::string foo{"text"};
std::array<char, 5u> fca;
std::copy(foo.data(), foo.data()+foo.size()+1u, fca.begin());
Pro
- proste
- pamięć stosu obsługa
Cons
- Static
- wymaga kopii ciągów
4. Surowa alokacja pamięci z automatycznym usuwaniem pamięci
std::string foo{ "text" };
auto p = std::make_unique<char[]>(foo.size()+1u);
std::copy(foo.data(), foo.data() + foo.size() + 1u, &p[0]);
Pro
- mały ślad pamięci
- Automatyczne usuwanie
- proste
Cons
- wymaga kopii ciągów
- statyczne (dynamiczne użycie wymaga dużo więcej kodu)
- mniej funkcji niż wektor lub tablica
5. Surowa alokacja pamięci z ręczną obsługą
std::string foo{ "text" };
char * p = nullptr;
try
{
p = new char[foo.size() + 1u];
std::copy(foo.data(), foo.data() + foo.size() + 1u, p);
// handle stuff with p
delete[] p;
}
catch (...)
{
if (p) { delete[] p; }
throw;
}
Pro
- maksymalna "kontrola"
Con
- wymaga kopii ciągów
- maksymalna odpowiedzialność / podatność na błędy
- Kompleks
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-30 11:10:21
Pracuję z API z wieloma funkcjami get as an input a char*
.
Stworzyłem małą klasę, aby stawić czoła tego typu problemom, zaimplementowałem idiom RAII.
class DeepString
{
DeepString(const DeepString& other);
DeepString& operator=(const DeepString& other);
char* internal_;
public:
explicit DeepString( const string& toCopy):
internal_(new char[toCopy.size()+1])
{
strcpy(internal_,toCopy.c_str());
}
~DeepString() { delete[] internal_; }
char* str() const { return internal_; }
const char* c_str() const { return internal_; }
};
I możesz go użyć jako:
void aFunctionAPI(char* input);
// other stuff
aFunctionAPI("Foo"); //this call is not safe. if the function modified the
//literal string the program will crash
std::string myFoo("Foo");
aFunctionAPI(myFoo.c_str()); //this is not compiling
aFunctionAPI(const_cast<char*>(myFoo.c_str())); //this is not safe std::string
//implement reference counting and
//it may change the value of other
//strings as well.
DeepString myDeepFoo(myFoo);
aFunctionAPI(myFoo.str()); //this is fine
Nazwałem klasę DeepString
, ponieważ tworzy głęboką i unikalną kopię (DeepString
nie można skopiować) istniejącego ciągu znaków.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-06 07:45:40
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-12 12:12:01
Zobacz też:]}
string str1("stackoverflow");
const char * str2 = str1.c_str();
Należy jednak pamiętać, że zwróci to const char *
.Dla char *
, Użyj strcpy
, aby skopiować ją do innej tablicy char
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-06 07:46:00
Spróbuj tego
std::string s(reinterpret_cast<const char *>(Data), Size);
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-17 16:45:29