cadena c_str () frente a datos ()

102

He leído en varios lugares que la diferencia entre c_str()y data()(en STL y otras implementaciones) es que c_str()siempre termina en nulo mientras data()que no. Por lo que he visto en implementaciones reales, hacen lo mismo o data()llaman c_str().

¿Que me estoy perdiendo aqui? ¿Cuál es más correcto de usar en qué escenarios?

Lobo
fuente

Respuestas:

105

La documentación es correcta. Úselo c_str()si desea una cadena terminada en nulo.

Si los implementadores lograron implementar data()en términos de c_str()usted, no tiene que preocuparse, aún use data()si no necesita que la cadena tenga una terminación nula, en alguna implementación puede resultar mejor que c_str ().

las cadenas no tienen que estar compuestas necesariamente por datos de caracteres, pueden estar compuestas por elementos de cualquier tipo. En esos casos data()es más significativo. c_str()en mi opinión, solo es realmente útil cuando los elementos de su cadena están basados ​​en caracteres.

Extra : En C ++ 11 en adelante, ambas funciones deben ser iguales. dataes decir, ahora se requiere que tenga terminación nula. Según cppreference : "La matriz devuelta tiene terminación nula, es decir, data () y c_str () realizan la misma función".

Scott Langham
fuente
4
Extra 2: en C ++ 17 en adelante, ahora también hay una sobrecarga no constante para .data(), por lo que ya no son equivalentes para cadenas no constantes.
Deduplicador
29

En C ++ 11 / C ++ 0x , data()ya c_str()no es diferente. Y, por lo tanto, también data()se requiere tener una terminación nula al final.

21.4.7.1 descriptores de basic_stringacceso [string.accessors]

const charT* c_str() const noexcept;

const charT* data() const noexcept;

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


21.4.5 acceso al elemento basic_string [string.access]

const_reference operator[](size_type pos) const noexcept;

1 Requiere: pos <= tamaño (). 2 Devuelve:, de lo *(begin() + pos) if pos < size()contrario charT();no se modificará una referencia a un objeto de tipo T con valor el valor referenciado.

mfazekas
fuente
¿Qué pasa si la cadena está compuesta de datos que no son caracteres, lo cual es legal para los datos de cadena AFAIK, incluido el nulo?
taz
3
@taz Incluso cuando se almacenan datos binarios, C ++ 11 requiere que std::stringasigne un extra charpara un seguimiento '\0'. Cuando lo haga std::string s("\0");, ambos s.data()[0]y s.data()[1]están garantizados para evaluar a 0.
bcrist
19

Incluso sabiendo que ha visto que ellos hacen lo mismo, o que .data () llama a .c_str (), no es correcto suponer que este será el caso de otros compiladores. También es posible que su compilador cambie con una versión futura.

2 razones para usar std :: string:

std :: string se puede usar tanto para texto como para datos binarios arbitrarios.

//Example 1
//Plain text:
std::string s1;
s1 = "abc";

//Example 2
//Arbitrary binary data:
std::string s2;
s2.append("a\0b\0b\0", 6);

Debe usar el método .c_str () cuando esté usando su cadena como ejemplo 1.

Debe usar el método .data () cuando esté usando su cadena como ejemplo 2. No porque sea peligroso usar .c_str () en estos casos, sino porque es más explícito que está trabajando con datos binarios para otros revisando tu codigo.

Posible error al usar .data ()

El siguiente código es incorrecto y podría causar una falla de segmento en su programa:

std::string s;
s = "abc";   
char sz[512]; 
strcpy(sz, s.data());//This could crash depending on the implementation of .data()

¿Por qué es común que los implementadores hagan que .data () y .c_str () hagan lo mismo?

Porque es más eficiente hacerlo. La única forma de hacer que .data () devuelva algo que no sea terminado en nulo, sería hacer que .c_str () o .data () copie su búfer interno, o simplemente use 2 búferes. Tener un solo búfer terminado en nulo siempre significa que siempre puede usar solo un búfer interno al implementar std :: string.

Brian R. Bondy
fuente
6
En realidad, el punto de .data () es que no debería copiar el búfer interno. Esto significa que una implementación no tiene que desperdiciar un carácter en \ 0 hasta que sea necesario. Nunca querrás dos búferes: si SÍ llamas .c_str (), agrega un \ 0 al búfer. .data () todavía puede devolver ese búfer.
MSalters
2
Estoy totalmente de acuerdo en que sería ridículo utilizar 2 búferes. Sin embargo, ¿cómo sabe que es por eso que se pretendía .data?
Brian R. Bondy
@ BrianR.Bondy Probé este código: .. auto str = string {"Test \ 0String!" }; cout << "DATOS:" << str.data () << endl; La salida es "Prueba" y no toda la cadena. ¿Qué hice mal?
programador
La última parte es incorrecta, data y c_str podrían usar el mismo búfer sin que esté terminado en 0; c_str podría simplemente agregar el 0 en la primera llamada.
Recuerda a Monica el
heads up, c ++ 11 hizo .data () un alias para .c_str ()
hanshenrik
3

Ya se ha respondido, algunas notas sobre el propósito: Libertad de implementación.

std::stringlas operaciones, por ejemplo, iteración, concatenación y mutación de elementos, no necesitan el terminador cero. A menos que pase el stringa una función que espera una cadena terminada en cero, se puede omitir.

Esto permitiría que una implementación haga que las subcadenas compartan los datos de la cadena real: string::substrpodría contener internamente una referencia a los datos de la cadena compartidos y el rango de inicio / fin, evitando la copia (y la asignación adicional) de los datos de la cadena real. La implementación diferiría la copia hasta que llame a c_str o modifique cualquiera de las cadenas. Nunca se haría ninguna copia si se leyeran las cadenas involucradas.

(La implementación de copia en escritura no es muy divertida en entornos multiproceso, además, los ahorros típicos de memoria / asignación no valen la pena el código más complejo hoy en día, por lo que rara vez se hace).


Del mismo modo, string::datapermite una representación interna diferente, por ejemplo, una cuerda (lista enlazada de segmentos de cuerda). Esto puede mejorar significativamente las operaciones de inserción / reemplazo. nuevamente, la lista de segmentos tendría que colapsarse en un solo segmento cuando llame c_stro data.

Peterchen
fuente
2

Cita de ANSI ISO IEC 14882 2003(C ++ 03 Standard):

    21.3.6 basic_string string operations [lib.string.ops]

    const charT* c_str() const;

    Returns: A pointer to the initial element of an array of length size() + 1 whose first size() elements
equal the corresponding elements of the string controlled by *this and whose last element is a
null character specified by charT().
    Requires: The program shall not alter any of the values stored in the array. Nor shall the program treat the
returned value as a valid pointer value after any subsequent call to a non-const member function of the
class basic_string that designates the same object as this.

    const charT* data() const;

    Returns: If size() is nonzero, the member returns a pointer to the initial element of an array whose first
size() elements equal the corresponding elements of the string controlled by *this. If size() is
zero, the member returns a non-null pointer that is copyable and can have zero added to it.
    Requires: The program shall not alter any of the values stored in the character array. Nor shall the program
treat the returned value as a valid pointer value after any subsequent call to a non- const member
function of basic_string that designates the same object as this.
Mihran Hovsepyan
fuente
2

Todos los comentarios anteriores son coherentes, pero también me gustaría agregar que a partir de c ++ 17, str.data () devuelve un char * en lugar de const char *

Nam Vu
fuente
1
Ambos consty las non-constsobrecargas están disponibles desde C ++ 17.
Gupta