Cómo convertir un std :: string a const char * o char *?

¿Cómo puedo convertir una std::string en un char* o en un const char* ?

Si solo desea pasar una std::string a una función que necesita const char* , puede usar

 std::string str; const char * c = str.c_str(); 

Si desea obtener una copia escribible, como char * , puede hacerlo con esto:

 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; 

Editar : Tenga en cuenta que lo anterior no es una excepción segura. Si hay algo entre la new llamada y la llamada de delete , se perderá la memoria, ya que nada llamará a delete automáticamente. Hay dos formas inmediatas de resolver esto.

boost :: scoped_array

boost::scoped_array eliminará la memoria cuando salga del scope:

 std::string str; boost::scoped_array 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

Esta es la forma estándar (no requiere ninguna biblioteca externa). std::vector , que gestiona completamente la memoria por ti.

 std::string str; std::vector writable(str.begin(), str.end()); writable.push_back('\0'); // get the char* using &writable[0] or &*writable.begin() 

Dado, digamos …

 std::string x = "hello"; 

Obteniendo un `char *` o `const char *` de una `cadena`

Cómo obtener un puntero de carácter que sea válido mientras x permanece dentro del scope y no se modifica más

C ++ 11 simplifica las cosas; todos los siguientes dan acceso al mismo búfer de cadena interno:

 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... 

Todos los punteros anteriores tendrán el mismo valor : la dirección del primer carácter en el búfer. Incluso una cadena vacía tiene un “primer carácter en el búfer”, porque C ++ 11 garantiza mantener siempre un carácter terminador NUL / 0 extra después del contenido de cadena explícitamente asignado (por ejemplo, std::string("this\0that", 9) tendrá un búfer que "this\0that\0" ).

Dado cualquiera de los indicadores anteriores:

 char c = p[n]; // valid for n <= x.size() // ie you can safely read the NUL at p[x.size()] 

Solo para el puntero no const de &x[0] :

 p_x0_rw[n] = c; // valid for n <= x.size() - 1 // ie don't overwrite the implementation maintained NUL 

Escribir un NUL en otra parte de la cadena no cambia el size() la string size() ; string tienen permitido contener cualquier número de NUL; no reciben ningún tratamiento especial por std::string (lo mismo en C ++ 03).

En C ++ 03 , las cosas fueron considerablemente más complicadas (diferencias clave destacadas ):

  • x.data()

    • devuelve const char* al búfer interno de la cuerda que no fue requerido por el estándar para concluir con un NUL (es decir, podría ser ['h', 'e', 'l', 'l', 'o'] seguido de sin inicializar o valores de basura, con accesos accidentales a los mismos teniendo un comportamiento indefinido ).
      • x.size() caracteres x.size() son seguros de leer, es decir, x[0] a x[x.size() - 1]
      • para las cadenas vacías, tiene garantizado un puntero no nulo al que se puede agregar 0 con seguridad (¡hurra!), pero no debe desreferenciar ese puntero.
  • &x[0]

    • para cadenas vacías esto tiene un comportamiento indefinido (21.3.4)
      • por ejemplo, dado f(const char* p, size_t n) { if (n == 0) return; ...whatever... } f(const char* p, size_t n) { if (n == 0) return; ...whatever... } no debes llamar a f(&x[0], x.size()); cuando x.empty() - simplemente use f(x.data(), ...) .
    • de lo contrario, según x.data() pero:
      • para no const x esto produce un puntero no const char* ; puedes sobrescribir el contenido de la cadena
  • x.c_str()

    • devuelve const char* a una representación ASCIIZ (terminada en NUL) del valor (es decir ['h', 'e', ​​'l', 'l', 'o', '\ 0']).
    • aunque pocas implementaciones, si alguna, eligieron hacerlo, el Estándar C ++ 03 fue redactado para permitir a la implementación de cadenas la libertad de crear un buffer terminado en NUL distinto sobre la marcha , desde el buffer potencialmente no-NUL "expuesto" por x.data() y &x[0]
    • x.size() + 1 caracteres son seguros de leer.
    • seguro garantizado incluso para cadenas vacías (['\ 0']).

Consecuencias de acceder a índices legales externos

De cualquier forma que obtenga un puntero, no debe acceder a la memoria más allá del puntero que los caracteres garantizados presentes en las descripciones anteriores. Los bashs de hacerlo tienen un comportamiento indefinido , con una posibilidad muy real de lockings de aplicaciones y resultados de basura incluso para lecturas, y datos adicionales al por mayor, corrupción de stack y / o vulnerabilidades de seguridad para escrituras.

¿Cuándo se invalidan esos indicadores?

Si llama a alguna función de miembro de string que modifica la string o reserva capacidad adicional, los valores de puntero devueltos previamente por cualquiera de los métodos anteriores se invalidan . Puede usar esos métodos nuevamente para obtener otro puntero. (Las reglas son las mismas que para los iteradores en la string s).

Consulte también Cómo obtener un puntero de carácter válido incluso después de que x abandone el scope o se modifique más abajo ...

Entonces, ¿cuál es mejor usar?

Desde C ++ 11, use .c_str() para datos ASCIIZ y .data() para datos "binarios" (se explican más abajo).

En C ++ 03, use .c_str() menos que esté seguro de que .data() es adecuado, y prefiera .data() sobre &x[0] ya que es seguro para cadenas vacías ....

... trate de entender el progtwig lo suficiente como para usar data() cuando sea apropiado, o probablemente cometerá otros errores ...

El carácter ASCII NUL '\ 0' garantizado por .c_str() es utilizado por muchas funciones como un valor centinela que denota el final de los datos relevantes y de acceso seguro. Esto se aplica a las funciones solo C ++ como, por ejemplo, fstream::fstream(const char* filename, ...) y las funciones de shared-with-C como strchr() y printf() .

Dado que las .c_str() C ++ 03 .c_str() sobre el búfer devuelto son un superconjunto de .data() , siempre se puede usar de manera segura .c_str() , pero a veces las personas no lo hacen porque:

  • El uso de .data() comunica a otros progtwigdores que leen el código fuente que los datos no son ASCIIZ (más bien, estás usando la cadena para almacenar un bloque de datos (que algunas veces ni siquiera es realmente textual)), o que estás ' re pasando a otra función que lo trata como un bloque de datos "binarios". Esta puede ser una idea crucial para garantizar que los cambios en el código de otros progtwigdores continúen manejando los datos correctamente.
  • Solo C ++ 03: hay una pequeña posibilidad de que la implementación de su string necesite hacer una asignación de memoria extra y / o copiar datos para preparar el búfer terminado en NUL

Como una sugerencia adicional, si los parámetros de una función requieren el ( const ) char* pero no insisten en obtener x.size() , la función probablemente necesite una entrada ASCIIZ, entonces .c_str() es una buena opción (la función necesita para saber dónde termina el texto de alguna manera, así que si no es un parámetro separado, solo puede ser una convención como un prefijo de longitud o centinela o una longitud esperada fija).

Cómo obtener un puntero de carácter válido incluso después de que x abandone el scope o se modifique más

Tendrá que copiar el contenido de la string x a una nueva área de memoria fuera de x . Este búfer externo podría estar en muchos lugares, como otra string o variable de matriz de caracteres, puede o no tener un tiempo de vida diferente a x debido a estar en un ámbito diferente (por ejemplo, espacio de nombres, global, estático, montón, memoria compartida, memoria asignada archivo).

Para copiar el texto de std::string x en una matriz de caracteres independiente:

 // 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 old_x(x.data(), x.data() + x.size()); // without the NUL std::vector 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 (eg 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); 

Otras razones para querer un char* o const char* generado a partir de una string

Entonces, arriba ha visto cómo obtener un ( const ) char* y cómo hacer una copia del texto independiente de la string original, pero ¿qué puede hacer con él? Un puñado aleatorio de ejemplos ...

  • dar acceso al código "C" al texto de la string C ++, como en printf("x is '%s'", x.c_str());
  • copie el texto de x en un búfer especificado por el llamador de su función (por ej. strncpy(callers_buffer, callers_buffer_size, x.c_str()) ) o memoria volátil utilizada para E / S del dispositivo (por ejemplo, for (const char* p = x.c_str(); *p; ++p) *p_device = *p; )
  • añada el texto de x a una matriz de caracteres que ya contenga texto ASCIIZ (p. ej. strcat(other_buffer, x.c_str()) ); tenga cuidado de no sobrepasar el búfer (en muchas situaciones puede necesitar usar strncat )
  • devuelve un const char* o char* de una función (quizás por razones históricas: el cliente usa su API existente) o para la compatibilidad con C no desea devolver un std::string , pero sí desea copiar los datos de su string en algún lugar para la persona que llama)
    • tenga cuidado de no devolver un puntero que pueda ser desreferenciado por la persona que llama luego de que una variable de string local a la que apunta ese puntero haya dejado scope
    • algunos proyectos con objetos compartidos comstackdos / vinculados para diferentes implementaciones std::string (por ejemplo, STLport y comstackdor nativo) pueden pasar datos como ASCIIZ para evitar conflictos

Utilice el método .c_str() para const char * .

Puedes usar &mystring[0] para obtener un puntero char * , pero hay un par de gotcha: no necesariamente obtendrás una cadena terminada en cero, y no podrás cambiar el tamaño de la cadena. Especialmente debe tener cuidado de no agregar caracteres más allá del final de la cadena o obtendrá un desbordamiento del búfer (y un posible locking).

No había ninguna garantía de que todos los caracteres fueran parte del mismo búfer contiguo hasta C ++ 11, pero en la práctica todas las implementaciones conocidas de std::string funcionaron de esa manera; ver ¿”& s [0]” apunta a caracteres contiguos en std :: string? .

Tenga en cuenta que muchas funciones de miembros de string reasignarán el búfer interno e invalidarán los punteros que haya guardado. Es mejor usarlos inmediatamente y luego descartarlos.

C ++ 17

C ++ 17 (próximo estándar) cambia la sinopsis de la plantilla basic_string agregando una sobrecarga no const de data() :

charT* data() noexcept;

Devuelve: Un puntero p tal que p + i == & operador para cada i en [0, tamaño ()].


CharT const * desde std::basic_string

 std::string const cstr = { "..." }; char const * p = cstr.data(); // or .c_str() 

CharT * desde std::basic_string

 std::string str = { "..." }; char * p = str.data(); 

C ++ 11

CharT const * desde std::basic_string

 std::string str = { "..." }; str.c_str(); 

CharT * desde std::basic_string

Desde C ++ 11 en adelante, el estándar dice:

  1. Los objetos tipo char en un objeto basic_string se deben almacenar contiguamente. Es decir, para cualquier objeto basic_string s , la identidad &*(s.begin() + n) == &*s.begin() + n se mantendrá para todos los valores de n tales que 0 <= n < s.size() .

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

    Devuelve: *(begin() + pos) si pos < size() ; de lo contrario, una referencia a un objeto de tipo CharT con valor CharT() ; el valor referenciado no se modificará.


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

    Devuelve: Un puntero p tal que p + i == &operator[](i) para cada i en [0,size()] .

Hay varias formas posibles de obtener un puntero de carácter no const.

1. Utilice el almacenamiento contiguo de C ++ 11

 std::string foo{"text"}; auto p = &*foo.begin(); 

Pro

  • Simple y corto
  • Rápido (solo método sin copia)

Contras

  • La final '\0' no debe ser alterada / no necesariamente parte de la memoria no const.

2. Utilice std::vector

 std::string foo{"text"}; std::vector fcv(foo.data(), foo.data()+foo.size()+1u); auto p = fcv.data(); 

Pro

  • Sencillo
  • Manejo automático de la memoria
  • Dinámica

Contras

  • Requiere copia de cadena

3. Utilice std::array si N es tiempo de comstackción constante (y lo suficientemente pequeño)

 std::string foo{"text"}; std::array fca; std::copy(foo.data(), foo.data()+foo.size()+1u, fca.begin()); 

Pro

  • Sencillo
  • Manejo de la memoria de stack

Contras

  • Estático
  • Requiere copia de cadena

4. Asignación de memoria sin procesar con eliminación automática de almacenamiento

 std::string foo{ "text" }; auto p = std::make_unique(foo.size()+1u); std::copy(foo.data(), foo.data() + foo.size() + 1u, &p[0]); 

Pro

  • Huella de memoria pequeña
  • Eliminación automática
  • Sencillo

Contras

  • Requiere copia de cadena
  • Estático (el uso dynamic requiere mucho más código)
  • Menos funciones que vector o matriz

5. Asignación de memoria bruta con manejo manual

 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

  • Máximo 'control'

Estafa

  • Requiere copia de cadena
  • Máxima responsabilidad / susceptibilidad por errores
  • Complejo

Estoy trabajando con una API con muchas funciones y obtengo como entrada una char* .

He creado una clase pequeña para enfrentar este tipo de problema, he implementado el modismo 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_; } }; 

Y puedes usarlo como:

 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(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 

He llamado a la clase DeepString porque está creando una copia profunda y única ( DeepString no se puede copiar) de una cadena existente.

 char* result = strcpy((char*)malloc(str.length()+1), str.c_str()); 

Solo mira esto:

 string str1("stackoverflow"); const char * str2 = str1.c_str(); 

Sin embargo, tenga en cuenta que esto devolverá un const char * Para un char * , use strcpy para copiarlo en otra matriz char .

Prueba esto

 std::string s(reinterpret_cast(Data), Size);