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

895

¿Cómo puedo convertir una std::stringa una char*o una const char*?

usuario37875
fuente
2
En lugar de: char * writable = new char [str.size () + 1]; Puede usar char writable [str.size () + 1]; Entonces no necesita preocuparse por eliminar la escritura o el manejo de excepciones.
77
No puede usar str.size () a menos que el tamaño se conozca en el momento de la compilación, también podría desbordar su pila si el valor del tamaño fijo es enorme.
Paul
1
char * result = strcpy ((char *) malloc (str.length () + 1), str.c_str ());
cegprakash
77
@cegprakash strcpyy mallocno son realmente la forma de C ++.
boycy
44
No, pero char* dest = new char[str.length() + 1]; std::copy(str.begin(), str.end(), dest)sería más idiomático en C ++. strcpy()y malloc()no son incorrectos ni problemáticos, pero parece inconsistente usar una cadena de C ++ y recursos de biblioteca de C con equivalentes de C ++ en el mismo bloque de código.
boycy

Respuestas:

1057

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

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

Si desea obtener una copia grabable, 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 seguro para excepciones. Si algo entre la newllamada y la deletellamada se lanza, perderá memoria, ya que nada lo llamará deleteautomáticamente. Hay dos formas inmediatas de resolver esto.

boost :: scoped_array

boost::scoped_array borrará la memoria para ti al salir del alcance:

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

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

std::string str;
std::vector<char> writable(str.begin(), str.end());
writable.push_back('\0');

// get the char* using &writable[0] or &*writable.begin()
Johannes Schaub - litb
fuente
41
Simplemente use char * result = strdup (str.c_str ());
Jasper Bekkers
63
podría, pero strdup no es una función estándar de ac o c ++, es de posix :)
Johannes Schaub - litb
14
lo que probablemente preferiría en general es std :: vector <char> escribible (str.begin (), str.end ()); writable.push_back ('\ 0'); char * c = & escribible [0];
Johannes Schaub - litb
17
std :: copy es la forma c ++ de hacer esto, sin la necesidad de llegar al puntero de cadena. Intento evitar usar las funciones de C tanto como puedo.
Johannes Schaub - litb
16
A partir de C ++ 17, std::string::data()ahora devuelve a en CharT*lugar de a const CharT*. Podría ser una buena idea actualizar esta respuesta :)
Rakete1111
192

Dado decir ...

std::string x = "hello";

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

Cómo obtener un puntero de caracteres que sea válido mientras xpermanezca dentro del alcance y no se modifique 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();
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...

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 adicional después del contenido de cadena asignado explícitamente (por ejemplo std::string("this\0that", 9), tendrá una retención de búfer "this\0that\0").

Dado cualquiera de los punteros anteriores:

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

Solo para el no constpuntero p_writable_datay de &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

Escribir un NUL en otra parte de la cadena no cambia el string's size(); stringSe permite que contengan cualquier cantidad de NUL; no reciben ningún tratamiento especial por parte de std::string(lo mismo en C ++ 03).

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

  • x.data()

    • vuelve const char*al búfer interno de la cadena que el Estándar no requería para concluir con un NUL (es decir, podría ser ['h', 'e', 'l', 'l', 'o']seguido por valores no inicializados o basura, con accesos accidentales a los mismos que tienen un comportamiento indefinido ).
      • x.size()los caracteres son seguros de leer, es decir, a x[0]través dex[x.size() - 1]
      • para cadenas vacías, tiene garantizado algún puntero no NULL al que se puede agregar 0 de forma segura (¡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... }que no debe llamar f(&x[0], x.size());cuando x.empty(), solo use f(x.data(), ...).
    • de lo contrario, según x.data()pero:
      • para no- const xesto produce un no const char*puntero; puedes sobrescribir el contenido de la cadena
  • x.c_str()

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

Consecuencias de acceder a índices legales externos

De cualquier manera 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 intentos de hacerlo tienen un comportamiento indefinido , con una posibilidad muy real de bloqueos de aplicaciones y resultados de basura incluso para lecturas, y adicionalmente datos al por mayor, corrupción de pila y / o vulnerabilidades de seguridad para escrituras.

¿Cuándo se invalidan esos punteros?

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

Consulte también Cómo obtener un puntero de caracteres válido incluso después de que xdeja el alcance o se modifica más abajo ...

Entonces, ¿cuál es mejor usar?

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

En C ++ 03, el uso .c_str()a menos de que .data()sea adecuada, y prefieren .data()más &x[0], ya que es seguro para las cadenas vacías ....

... intente comprender el programa lo suficiente como para usarlo data()cuando sea apropiado, o probablemente cometerá otros errores ...

El carácter ASCII NUL '\ 0' garantizado por .c_str()muchas funciones lo utiliza como un valor centinela que denota el final de los datos relevantes y de acceso seguro. Esto se aplica tanto a C ++: solo funciones como say fstream::fstream(const char* filename, ...)y funciones compartidas con C como strchr(), y printf().

Dadas las .c_str()garantías de C ++ 03 sobre el búfer devuelto son un superconjunto de .data()'s, siempre se puede usar con seguridad .c_str(), pero a veces las personas no lo hacen porque:

  • el uso se .data()comunica con otros programadores que leen el código fuente de que los datos no son ASCIIZ (más bien, está utilizando la cadena para almacenar un bloque de datos (que a veces ni siquiera es realmente textual)), o que se los está pasando a Otra función que lo trata como un bloque de datos "binarios". Esto puede ser una idea crucial para garantizar que los cambios en el código de otros programadores continúen manejando los datos correctamente.
  • Solo C ++ 03: existe una pequeña posibilidad de que su stringimplementación necesite hacer una asignación de memoria adicional y / o copiar datos para preparar el búfer terminado en NUL

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

Cómo obtener un puntero de caracteres válido incluso después de xdejar el alcance o si se modifica más

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

Para copiar el texto de std::string xuna 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<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);

Otras razones para querer char*o const char*generarse a partir de unstring

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

  • dar acceso al código "C" al texto de C ++ string, como enprintf("x is '%s'", x.c_str());
  • copie xel texto en un búfer especificado por el llamador de su función (por ejemplo strncpy(callers_buffer, callers_buffer_size, x.c_str())), o la memoria volátil utilizada para la E / S del dispositivo (por ejemplo for (const char* p = x.c_str(); *p; ++p) *p_device = *p;)
  • anexe xel texto a una matriz de caracteres que ya contenga texto ASCIIZ (por ejemplo strcat(other_buffer, x.c_str())): tenga cuidado de no saturar el búfer (en muchas situaciones puede necesitar usar strncat)
  • devolver const char*ao char*desde una función (tal vez por razones históricas - el cliente está usando su API existente - o por compatibilidad con C que no desea devolver std::string, pero desea copiar sus stringdatos 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 después de que una stringvariable local a la que apuntó ese puntero haya dejado alcance
    • Algunos proyectos con objetos compartidos compilados / vinculados para diferentes std::stringimplementaciones (por ejemplo, STLport y compilador nativo) pueden pasar datos como ASCIIZ para evitar conflictos
Tony Delroy
fuente
44
Buena esa. Otra razón para querer un char * (no constante) es operar con transmisión MPI. Se ve mejor si no tiene que copiar de un lado a otro. Yo personalmente le hubiera ofrecido un char * const getter a string. Puntero constante, pero cadena editable. Aunque puede haber alterado la conversión implícita de const char * a string ...
bartgol
33

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

Puede usar &mystring[0]para obtener un char *puntero, pero hay un par de gotcha: no necesariamente obtendrá una cadena terminada en cero, y no podrá 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 bloqueo).

No había 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::stringtrabajaron de esa manera; ver ¿"& s [0]" apunta a caracteres contiguos en un std :: string? .

Tenga en cuenta que muchas stringfunciones miembro reasignarán el búfer interno e invalidarán los punteros que haya guardado. Lo mejor es usarlos de inmediato y luego desecharlos.

Mark Ransom
fuente
1
debe tener en cuenta que data () devuelve const char * :) lo que quiere decir es & str [0], que devuelve una cadena contigua, pero no necesariamente terminada en nulo.
Johannes Schaub - litb
1
@litb, Argh! Eso es lo que obtengo por tratar de obtener una respuesta rápida. He usado su solución en el pasado, no sé por qué no fue lo primero que se me ocurrió. He editado mi respuesta.
Mark Ransom
2
Técnicamente, el almacenamiento std :: string será contiguo solo en C ++ 0x.
MSalters
1
@MSalters, gracias, no lo sabía. Sin embargo, sería difícil encontrar una implementación donde ese no fuera el caso.
Mark Ransom
2
char * result = strcpy (malloc (str.length () + 1), str.c_str ());
cegprakash
21

C ++ 17

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

charT* data() noexcept;

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


CharT const * desde std::basic_string<CharT>

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

CharT * desde std::basic_string<CharT>

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

C ++ 11

CharT const * desde std::basic_string<CharT>

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

CharT * desde std::basic_string<CharT>

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

  1. Los objetos tipo char en un basic_stringobjeto se almacenarán contiguamente. Es decir, para cualquier basic_stringobjeto s, la identidad &*(s.begin() + n) == &*s.begin() + nse mantendrá para todos los valores de ntal 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 CharTcon 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 uno ide [0,size()].

Hay varias formas posibles de obtener un puntero de caracteres no constante.

1. Use el almacenamiento contiguo de C ++ 11

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

Pro

  • Simple y corto
  • Rápido (único método sin copia involucrada)

Contras

  • Final '\0'no debe ser alterado / no necesariamente parte de la memoria no constante.

2. Uso std::vector<CharT>

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

Pro

  • Simple
  • Manejo automático de memoria
  • Dinámica

Contras

  • Requiere copia de cadena

3. Use std::array<CharT, N>if Nes un tiempo de compilación constante (y lo suficientemente pequeño)

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

Pro

  • Simple
  • Manejo de memoria de pila

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<char[]>(foo.size()+1u);
std::copy(foo.data(), foo.data() + foo.size() + 1u, &p[0]);

Pro

  • Pequeña huella de memoria
  • Eliminación automática
  • Simple

Contras

  • Requiere copia de cadena
  • Estático (el uso dinámico requiere mucho más código)
  • Menos características que el vector o la matriz

5. Asignación de memoria sin formato 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
Pixelchemist
fuente
9

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

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

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

Alessandro Teruzzi
fuente
3
Evitaría esta convención de nomenclatura. c_str()como se usa stdes una abreviatura de "C-string" no "const string" y str()siempre devuelve un std::basic_string, no char*(por ejemplo std::stringstream::str())
bcrist
8
char* result = strcpy((char*)malloc(str.length()+1), str.c_str());
cegprakash
fuente
1
se ve elegante pero realmente difícil de entender ... Simple es la mejor OMI
Naeem A. Malik
44
strcpy (), malloc (), length () y c_str () son funciones básicas y no hay nada difícil en esto. Solo asignando memoria y copiando.
cegprakash
55
Si las funciones son básicas, pero se han retorcidas y dobladas a parecerse plato de espaguetis o un trazador de líneas monstruo de Frankenstein :)
Naeem A. Malik
44
Sí, las funciones son básicas pero ... ¿te acordaste de cuando comenzaste a lidiar con un lenguaje de programación? Algunas líneas más para explicar y realmente ayudarán a un neófito a aprender por qué, por ejemplo, es diferente o mejor que esta respuesta :)
Hastur
2
@cegprakash: Siempre que haya un malloc (), también debe haber un free (). De lo contrario, el código pierde memoria, y también la solución en su respuesta. Asignar memoria sin al menos insinuar la desasignación requerida es una mala práctica para tales preguntas.
Striezel
7

Solo mira esto:

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

Sin embargo, tenga en cuenta que esto devolverá a const char *.

Para char * , use strcpypara copiarlo en otra charmatriz.

devsaw
fuente
23
Hola, lo que publicaste ya se ha dicho varias veces, con más detalles, en otras respuestas a la pregunta de 5 años. Está bien responder preguntas anteriores, pero solo si agrega nueva información. De lo contrario, es solo ruido.
Mat
77
Personalmente, aprecio la simplicidad.
TankorSmash
-4

Prueba esto

std::string s(reinterpret_cast<const char *>(Data), Size);
anish
fuente