stringa c_str () vs. data ()


102

Ho letto in diversi punti che la differenza tra c_str()e data()(in STL e altre implementazioni) è che c_str()è sempre terminato con null mentre data()non lo è. Per quanto ho visto nelle implementazioni effettive, fanno lo stesso o data()chiamano c_str().

Cosa mi manca qui? Quale è più corretto da usare in quali scenari?

Risposte:


105

La documentazione è corretta. Utilizzare c_str()se si desidera una stringa con terminazione null.

Se gli implementatori riescono a implementare data()in termini di c_str()te non devi preoccuparti, usa ancora data()se non hai bisogno che la stringa sia terminata con null, in alcune implementazioni potrebbe risultare migliore di c_str ().

le stringhe non devono necessariamente essere composte da dati di caratteri, potrebbero essere composte da elementi di qualsiasi tipo. In quei casi data()è più significativo. c_str()secondo me è veramente utile solo quando gli elementi della tua stringa sono basati sui caratteri.

Extra : in C ++ 11 in poi, entrambe le funzioni devono essere le stesse. ie dataora deve essere terminato con null. Secondo cppreference : "L'array restituito è a terminazione null, ovvero data () e c_str () svolgono la stessa funzione."


4
Extra 2: in C ++ 17 in poi, ora c'è anche un sovraccarico non const per .data(), quindi non sono più equivalenti per le stringhe non costanti.
Deduplicatore

29

In C ++ 11 / C ++ 0x , data()e c_str()non è diverso. E quindi data()è richiesta anche una terminazione nulla alla fine.

21.4.7.1 basic_stringaccessors [string.accessors]

const charT* c_str() const noexcept;

const charT* data() const noexcept;

1 Restituisce: un puntatore p tale che p + i == &operator[](i)per ogni iin [0,size()].


21.4.5 basic_string element access [string.access]

const_reference operator[](size_type pos) const noexcept;

1 Richiede: pos <= size (). 2 Restituisce:, *(begin() + pos) if pos < size()altrimenti un riferimento ad un oggetto di tipo T con valore charT();il valore di riferimento non deve essere modi fi cato.


Cosa succede se la stringa è composta da dati non di caratteri, che è legale per i dati di stringa AFAIK, incluso null?
taz

3
@taz Anche quando si archiviano dati binari, C ++ 11 richiede che std::stringallochi un extra charper un finale '\0'. Quando lo fai std::string s("\0");, entrambi s.data()[0]e s.data()[1]sono garantiti per valutare 0.
bcrist

19

Anche sapendo che hai visto che fanno lo stesso, o che .data () chiama .c_str (), non è corretto presumere che questo sarà il caso di altri compilatori. È anche possibile che il tuo compilatore cambi con una versione futura.

2 motivi per utilizzare std :: string:

std :: string può essere utilizzato sia per testo che per dati binari arbitrari.

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

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

Dovresti usare il metodo .c_str () quando usi la tua stringa come esempio 1.

Dovresti usare il metodo .data () quando usi la tua stringa come esempio 2. Non perché sia ​​pericoloso usare .c_str () in questi casi, ma perché è più esplicito che stai lavorando con dati binari per altri il tuo codice.

Possibile insidia con l'utilizzo di .data ()

Il codice seguente è sbagliato e potrebbe causare un segfault nel tuo programma:

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

Perché è comune che gli implementatori facciano la stessa cosa .data () e .c_str ()?

Perché è più efficiente farlo. L'unico modo per fare in modo che .data () restituisca qualcosa che non è terminato da null, sarebbe avere .c_str () o .data () copiare il loro buffer interno, o usare solo 2 buffer. Avere un singolo buffer con terminazione null significa sempre che puoi sempre usare un solo buffer interno quando implementi std :: string.


6
In realtà, il punto di .data () è che non dovrebbe copiare il buffer interno. Ciò significa che un'implementazione non deve sprecare un carattere su \ 0 finché non è necessario. Non vorrai mai due buffer: se chiami .c_str (), aggiungi \ 0 al buffer. .data () può ancora restituire quel buffer.
MSalters

2
D'accordo pienamente sarebbe ridicolo usare 2 buffer. Come fai a sapere che è per questo che .data è stato pensato?
Brian R. Bondy

@ BrianR.Bondy Ho provato questo codice: .. auto str = string {"Test \ 0String!" }; cout << "DATA:" << str.data () << endl; L'output è "Test" e non l'intera stringa, cosa ho fatto di sbagliato?
programmatore

L'ultima parte è sbagliata, data e c_str potrebbero usare lo stesso buffer senza che venga terminato con 0 - c_str potrebbe semplicemente aggiungere lo 0 alla prima chiamata.
Ricorda Monica

testa a testa, c ++ 11 ha reso .data () un alias per .c_str ()
hanshenrik

3

È già stata data una risposta, alcune note sullo scopo: Libertà di attuazione.

std::stringle operazioni - es. iterazione, concatenazione e mutazione di elementi - non hanno bisogno del terminatore zero. A meno che non passi ilstring a una funzione che prevede una stringa terminata con zero, può essere omesso.

Ciò consentirebbe a un'implementazione di condividere le sottostringhe dei dati della stringa effettiva: string::substr potrebbe contenere internamente un riferimento ai dati della stringa condivisa e l'intervallo di inizio / fine, evitando la copia (e l'allocazione aggiuntiva) dei dati della stringa effettiva. L'implementazione rimanda la copia fino a quando non si chiama c_str o si modifica una qualsiasi delle stringhe. Nessuna copia sarebbe mai stata fatta se le striscioni coinvolte fossero solo lette.

(L'implementazione del copy-on-write non è molto divertente negli ambienti multithread, inoltre il tipico risparmio di memoria / allocazione non vale il codice più complesso oggi, quindi è raramente fatto).


Allo stesso modo, string::dataconsente una diversa rappresentazione interna, ad esempio una corda (elenco collegato di segmenti di stringa). Ciò può migliorare notevolmente le operazioni di inserimento / sostituzione. di nuovo, l'elenco dei segmenti dovrebbe essere compresso in un singolo segmento quando chiami c_stro data.


2

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

2

Tutti i commenti precedenti sono coerenza, ma vorrei anche aggiungere che a partire da c ++ 17, str.data () restituisce un carattere * invece di un carattere const *


1
Entrambi conste non-constsovraccarichi sono disponibili da C ++ 17.
Gupta
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.