Jak przekonwertować std:: string na const char * lub char*?

Jak mogę przekonwertować std::string na char* lub const char*?

Author: Niall, 2008-12-07

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()
 948
Author: Johannes Schaub - litb,
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] przez x[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.
  • &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 call f(&x[0], x.size()); when x.empty() - just użyj f(x.data(), ...).
    • inaczej, jak na x.data() ale:
      • dla non-const x to daje nie-const char* wskaźnik; można nadpisać zawartość ciągu
  • 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']).

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 w printf("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życie strncat)
  • zwraca const char* lub char* 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ć dane string 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
 174
Author: Tony Delroy,
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ć.

 31
Author: Mark Ransom,
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:

  1. obiekty podobne do znaków w obiekcie basic_string powinny być przechowywane obok siebie. Oznacza to, że dla dowolnego basic_string obiektu s tożsamość {[22] } powinna posiadać dla wszystkich wartości n takie, że 0 <= n < s.size().

  1. const_reference operator[](size_type pos) const;
    reference operator[](size_type pos);

    Zwraca: *(begin() + pos) if pos < size(), w przeciwnym razie odniesienie do obiektu typu CharT z wartością CharT(); wartość odniesienia nie może być modyfikowana.


  1. const charT* c_str() const noexcept;
    const charT* data() const noexcept;

    Zwraca: wskaźnik p taki, że p + i == &operator[](i) dla każdego i 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
 18
Author: Pixelchemist,
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.

 9
Author: Alessandro Teruzzi,
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());
 7
Author: cegprakash,
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.

 7
Author: devsaw,
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);
 -4
Author: anish,
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