Cómo convertir una cadena std::a const char* o char*?


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

Author: Niall, 2008-12-07

8 answers

Si solo quieres pasar un std::string para 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 se lanza algo entre la llamada new y la llamada delete, perderá memoria, ya que nada llamará a delete automáticamente. Hay dos formas inmediatas de resolver este.

Boost:: scoped_array

boost::scoped_array borrará la memoria para usted 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). Usted usa std::vector, que gestiona completamente la memoria para usted.

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-06 07:44:34

Dado decir...

std::string x = "hello";

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

Cómo obtener un puntero de carácter que sea válido mientras x permanezca en el ámbito y no se modifique más

C++11 simplifica las cosas; lo siguiente da 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 de 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 contenga "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 puntero no - const de &x[0]:

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 hace no cambiar el string's size(); string's se les permite contener cualquier número de NULs - no se les da ninguna especial tratamiento por std::string (igual en C++03).

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

  • x.data()

    • devuelve const char* a la cadena de búfer interno la cual no era requerida por la Norma para concluir con un NUL (es decir, podría ser ['h', 'e', 'l', 'l', 'o'], seguido por no inicializado o valores de basura, con accesos accidentales de los mismos teniendo indefinido comportamiento).
      • x.size() los caracteres son seguros de leer, es decir, x[0] hasta x[x.size() - 1]
      • para cadenas vacías, se le garantiza algún puntero no NULO al que se pueda agregar 0 de forma segura (¡hurra!), pero no deberías 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... } no debe llamar f(&x[0], x.size()); cuando x.empty() - solo utilizar f(x.data(), ...).
    • de lo contrario, según x.data() pero:
      • para no-const x esto produce un no-const char* puntero; puede sobrescribir el contenido de 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 optaron por hacerlo, el estándar C++03 fue redactado para permitir la cadena implementación la libertad de crear una búfer distinto con terminación nula sobre la marcha , desde el búfer potencialmente no nulo "expuesto" por x.data() y &x[0]
    • x.size() + 1 caracteres son seguros de leer.
    • garantizado seguro incluso para cadenas vacías (['\0']).

Consecuencias del acceso a índices jurídicos externos

Sea cual sea la forma en que obtenga un puntero, no debe acceder a la memoria más puntero a 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 además 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 función miembro string que modifica la string o reserva más capacidad, se devuelve cualquier valor de puntero de antemano por cualquiera de los métodos anteriores son invalidado. Puede usar esos métodos de nuevo para obtener otro puntero. (Las reglas son las mismas que para los iteradores en string s).

Véase también Cómo obtener un puntero de carácter válido incluso después de que x deje el ámbito o se modifique más a continuación....

Entonces, ¿cuál es mejor para usar?

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

En C++03, use .c_str() a 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 programa 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 tanto a funciones de solo C++como say fstream::fstream(const char* filename, ...) y funciones compartidas con C como strchr() y printf().

Dado que las garantías de C++03 .c_str() sobre el búfer devuelto son un súper conjunto de .data(), siempre puede usar .c_str() de forma segura, pero la gente a veces no lo hace porque:

  • usando .data() comunica a otros programadores 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 a veces ni siquiera es realmente textual)), o que lo estás 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 de código de otros programadores continúen manejando los datos correctamente.
  • Solo C++03: hay una pequeña posibilidad de que su implementación string necesite hacer alguna asignación de memoria adicional y / o copia de datos para preparar el búfer terminado NUL

Como sugerencia adicional, si los parámetros de una función requieren (const) char* pero no insista en obtener 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 un parámetro separado, solo puede ser una convención como un prefijo de longitud o sentinel o alguna longitud esperada fija).

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

Necesitarás copiar el contenido de la string x a un nuevo á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 una vida útil diferente a x debido a estar en un ámbito diferente (por ejemplo, espacio de nombres, global, estático, montón, memoria compartida, archivo mapeado de memoria).

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<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 un char* o const char* generado a partir de un string

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

  • da acceso al código" C " al texto de C++ string, como en printf("x is '%s'", x.c_str());
  • copie el texto de x a un búfer especificado por el llamante de su función (por ejemplo, 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;)
  • anexe el texto de x a una matriz de caracteres que ya contenga algo de texto ASCIIZ (por ejemplo, strcat(other_buffer, x.c_str())) - tenga cuidado de no sobrepasar el búfer (en muchas situaciones es posible que necesite usar strncat)
  • devuelve un const char* o char* desde una función (quizás por razones históricas-el cliente usa su API existente-o por compatibilidad con C no desea devolver un std::string, pero sí desea copiar los datos de su string en algún lugar para el llamante)
    • tenga cuidado de no devolver un puntero que pueda ser desreferenciado por el llamante después de una variable local string a la que apuntó ese puntero ha dejado ámbito
    • algunos los proyectos con objetos compartidos compilados / vinculados para diferentes implementaciones std::string (por ejemplo, STLPort y compiler-native) pueden pasar datos como ASCIIZ para evitar conflictos
 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-11 16:59:05

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

Puede usar &mystring[0] para obtener un puntero char *, 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 debes tener cuidado de no agregar caracteres más allá del final de la cadena o obtendrás un desbordamiento de búfer (y un probable bloqueo).

No había garantía de que todos los caracteres serían parte del mismo búfer contiguo hasta C++11, pero en practique todas las implementaciones conocidas de std::string funcionaron de esa manera de todos modos; vea ¿"&s[0]" apunta a caracteres contiguos en una cadena std::?.

Tenga en cuenta que muchas funciones miembro string reasignarán el búfer interno e invalidarán cualquier puntero que haya guardado. Lo mejor es usarlos inmediatamente y luego desecharlos.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:23

C++17

C++17 (próximo estándar) cambia la sinopsis de la plantilla basic_string añadiendo 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 * de std::basic_string<CharT>

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

CharT * de std::basic_string<CharT>

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

C++11

CharT const * de std::basic_string<CharT>

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

CharT * de std::basic_string<CharT>

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

  1. Los objetos similares a caracteres en un objeto basic_string se almacenarán contiguamente. Es decir, para cualquier basic_string objeto 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 maneras posibles de obtener un puntero de carácter no constante.

1. Utilizar 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 involucrada)

Cons

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

2. Use 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ámico

Cons

  • Requiere una copia de cadena

3. Use std::array<CharT, N> si N es constante de tiempo de compilación (y lo suficientemente pequeña)

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

Pro

  • Simple
  • Pila de memoria manipulación

Cons

  • Estática
  • Requiere una copia de cadena

4. Asignación de memoria raw 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

Cons

  • Requiere una copia de cadena
  • Estático (el uso dinámico requiere mucho más código)
  • Menos entidades que vector o array

5. Asignación de memoria raw 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

  • "control" máximo

Con

  • Requiere una copia de cadena
  • Máxima responsabilidad / susceptibilidad a errores
  • Complejo
 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-30 11:10:21

Estoy trabajando con una API con muchas funciones que obtienen como entrada un char*.

He creado una clase pequeña para hacer frente a este tipo de problemas, 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 DeepString porque está creando una copia profunda y única (el DeepString no se puede copiar) de una cadena existente.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-12 12:12:01

Solo vea 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 otro array 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-06 07:46:00

Prueba esto

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-17 16:45:29