c++ - c_str - string to const char* arduino




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

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


C ++ 17

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

charT* data() noexcept;

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

CharT const * from std::basic_string<CharT>

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

CharT * from std::basic_string<CharT>

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

C ++ 11

CharT const * from std::basic_string<CharT>

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

CharT * from std::basic_string<CharT>

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

  1. Los objetos de tipo char en un objeto basic_string se almacenarán de forma contigua. 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) if pos < size() , de lo contrario, es una referencia a un objeto de tipo CharT con valor CharT() ; El valor referenciado no será modificado.

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

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 involucrada)

Contras

  • El '\0' final no debe modificarse / no es necesariamente parte de la memoria no constante.

2. Utilice std::vector<CharT>

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

Pro

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

Contras

  • Requiere copia de cadena

3. Use std::array<CharT, N> si N es una 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

  • Sencillo
  • Manejo de memoria de pila

Contras

  • Estático
  • Requiere copia de cadena

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

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

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 en bruto 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

  • Maximo 'control'

Estafa

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

Dado lo dicho ...

std::string x = "hello";

Obteniendo un `char *` o `const char *` de un `string`

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

C ++ 11 simplifica las cosas; Todos los siguientes dan acceso al mismo buffer interno de cadenas:

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 mantendrá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 la cadena asignado explícitamente (por ejemplo, std::string("this\0that", 9) tendrá un búfer que contiene "this\0that\0" ).

Teniendo en cuenta 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 no cambia el size() la string size() ; string pueden contener cualquier número de NUL; no se les da ningún tratamiento especial por std::string (igual en C ++ 03).

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

  • x.data()

    • devuelve const char* al búfer interno de la cadena 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 uninitialised o valores de basura, con accesos accidentales que tienen un comportamiento indefinido ).
      • x.size() caracteres x.size() son seguros de leer, es decir, de x[0] a x[x.size() - 1]
      • para cadenas vacías, se le garantiza un puntero no NULL al que se puede agregar con seguridad 0 (¡hurra!), pero no debe desreferir 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 f(&x[0], x.size()); cuando x.empty() - simplemente use f(x.data(), ...) .
    • de lo contrario, según x.data() pero:
      • para x no const esto produce un puntero char* 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 la cadena la libertad de crear un búfer distinto terminado en NUL sobre la marcha , desde el búfer 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 del acceso a índices jurídicos externos.

De cualquier forma que obtenga un puntero, no debe acceder a la memoria desde el puntero más allá de los caracteres 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 las lecturas y, además, mayorista de datos, daños en la pila y / o vulnerabilidades de seguridad para las escrituras.

¿Cuándo se invalidan esos punteros?

Si llama a alguna función de miembro de string que modifica la string o reserva una capacidad adicional, cualquier valor de puntero devuelto de antemano por cualquiera de los métodos anteriores se invalida . Puedes 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 salga del alcance o se modifique más adelante ...

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, 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 programa lo suficiente como para usar data() cuando sea apropiado, o probablemente cometerá otros errores ...

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

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

  • el uso de .data() comunica a 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 re pasándolo a otra función que lo trata como un bloque de datos "binarios". Esta puede ser una visión crucial para garantizar que los cambios de código de otros programadores continúen manejando los datos correctamente.
  • Solo C ++ 03: existe una pequeña posibilidad de que la implementación de su string deba realizar alguna asignación de memoria adicional y / o copia de datos para preparar el búfer terminado en NUL

Como una sugerencia adicional, si los parámetros de una función requieren ( const ) char* pero no insisten en obtener x.size() , la función probablemente necesite una entrada ASCIIZ, por lo que .c_str() es una buena opción (la función necesita para 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 centinela o una longitud esperada fija.

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

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

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 desde una string

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

  • dé el acceso del 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 ejemplo, strncpy(callers_buffer, callers_buffer_size, x.c_str()) ), o memoria volátil utilizada para la E / S del dispositivo (por ejemplo, for (const char* p = x.c_str(); *p; ++p) *p_device = *p; )
  • adjunte el texto de x a una matriz de caracteres que ya contenga algún texto ASCIIZ (p. ej., strcat(other_buffer, x.c_str()) ) - tenga cuidado de no sobrepasar el búfer (en muchas situaciones es posible que necesite usar strncat )
  • devuelva un const char* o char* desde una función (quizás por razones históricas - clientes usando su API existente) o por compatibilidad con C, no quiere devolver una 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 desreferido por la persona que llama después de que una variable de string local a la que apuntó dicho puntero haya dejado alcance
    • algunos proyectos con objetos compartidos compilados / vinculados para diferentes implementaciones de std::string (por ejemplo, STLport y nativo del compilador) pueden pasar datos como ASCIIZ para evitar conflictos

Prueba esto

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

Si solo quiere pasar un 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 de escritura, 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 delete lanzamientos de llamadas, perderá 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 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). Utiliza std::vector , que administra la memoria completamente 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()

Use 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, debe tener cuidado de no agregar caracteres más allá del final de la cadena o obtendrá una saturación del búfer (y un posible bloqueo).

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 una cadena std ::? .

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


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






const