string c_str () vs. dados ()

102

Eu li vários lugares que a diferença entre c_str()e data()(em STL e outras implementações) é que c_str()é sempre terminado em nulo, enquanto data()não é. Pelo que tenho visto em implementações reais, eles fazem o mesmo ou data()chamam c_str().

O que estou perdendo aqui? Qual é o mais correto para usar em quais cenários?

Lobo
fonte

Respostas:

105

A documentação está correta. Use c_str()se quiser uma string terminada em nulo.

Se os implementadores começaram a implementar data()em termos de c_str()você não precisa se preocupar, ainda use data()se não precisar que a string seja terminada em nulo, em algumas implementações pode acabar tendo um desempenho melhor do que c_str ().

strings não precisam necessariamente ser compostas de dados de caracteres, elas podem ser compostas com elementos de qualquer tipo. Nesses casos, data()é mais significativo. c_str()na minha opinião, só é realmente útil quando os elementos de sua string são baseados em caracteres.

Extra : no C ++ 11 em diante, ambas as funções devem ser as mesmas. ou dataseja, agora é necessário ter terminação nula. De acordo com cppreference : "O array retornado é terminado em nulo, ou seja, data () e c_str () executam a mesma função."

Scott Langham
fonte
4
Extra 2: no C ++ 17 em diante, agora também há uma sobrecarga não constante para .data(), portanto, eles não são mais equivalentes para strings não constantes.
Desduplicador de
29

Em C ++ 11 / C ++ 0x , data()e c_str()não é mais diferente. E, portanto, também data()é necessário ter uma terminação nula no final.

21.4.7.1 basic_stringacessores [string.accessors]

const charT* c_str() const noexcept;

const charT* data() const noexcept;

1 Retorna: um ponteiro p tal que p + i == &operator[](i)para cada ipol [0,size()].


21.4.5 acesso ao elemento basic_string [string.access]

const_reference operator[](size_type pos) const noexcept;

1 Requer: pos <= size (). 2 Retorna:, *(begin() + pos) if pos < size()caso contrário, uma referência a um objeto do tipo T com valor, charT();o valor referenciado não deve ser modificado.

Mfazekas
fonte
E se a string for composta de dados sem caracteres, o que é válido para dados de string AFAIK, incluindo nulos?
taz
3
@taz Mesmo ao armazenar dados binários, C ++ 11 requer que std::stringaloque um extra charpara um rastreamento '\0'. Quando você fizer isso std::string s("\0");, ambos s.data()[0]e s.data()[1]
terão
19

Mesmo sabendo que você viu que eles fazem o mesmo, ou que .data () chama .c_str (), não é correto assumir que esse será o caso para outros compiladores. Também é possível que seu compilador mude com uma versão futura.

2 razões para usar std :: string:

std :: string pode ser usado para texto e dados binários arbitrários.

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

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

Você deve usar o método .c_str () quando estiver usando sua string como exemplo 1.

Você deve usar o método .data () quando estiver usando sua string como exemplo 2. Não porque seja perigoso usar .c_str () nesses casos, mas porque é mais explícito que você está trabalhando com dados binários para outros revisarem seu código.

Possível armadilha com o uso de .data ()

O código a seguir está errado e pode causar um segfault em seu programa:

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

Por que é comum que os implementadores façam .data () e .c_str () fazerem a mesma coisa?

Porque é mais eficiente fazer isso. A única maneira de fazer .data () retornar algo que não seja terminado em nulo, seria ter .c_str () ou .data () copiar seu buffer interno, ou usar apenas 2 buffers. Ter um único buffer terminado em null sempre significa que você sempre pode usar apenas um buffer interno ao implementar std :: string.

Brian R. Bondy
fonte
6
Na verdade, o objetivo de .data () é que ele não deve copiar o buffer interno. Isso significa que uma implementação não precisa desperdiçar um caractere em \ 0 até que seja necessária. Você nunca iria querer dois buffers: se você chamar .c_str (), acrescente um \ 0 ao buffer. .data () ainda pode retornar esse buffer.
MSalters
2
Concordo plenamente que seria ridículo usar 2 buffers. Como você sabe que é por isso que .data foi planejado?
Brian R. Bondy,
@ BrianR.Bondy tentei este código: .. auto str = string {"Test \ 0String!" }; cout << "DADOS:" << str.data () << endl; A saída é "Teste" e não a string inteira. O que eu fiz de errado?
programador
A última parte está errada, data e c_str poderiam usar o mesmo buffer sem ser finalizado em 0 - c_str poderia simplesmente adicionar 0 na primeira chamada.
Lembre
alerta, c ++ 11 tornou .data () um alias para .c_str ()
hanshenrik
3

Já foi respondido, algumas notas sobre o objetivo: Liberdade de implementação.

std::stringoperações - por exemplo, iteração, concatenação e mutação de elemento - não precisam do terminador zero. A menos que você passe o stringpara uma função que espera uma string terminada em zero, ele pode ser omitido.

Isso permitiria que uma implementação tivesse substrings compartilhando os dados reais da string: string::substrpoderia conter internamente uma referência aos dados da string compartilhada e o intervalo inicial / final, evitando a cópia (e alocação adicional) dos dados reais da string. A implementação iria adiar a cópia até que você chame c_str ou modifique qualquer uma das strings. Nenhuma cópia jamais seria feita se os strigns envolvidos fossem apenas lidos.

(a implementação copy-on-write não é muito divertida em ambientes multithread, além da economia típica de memória / alocação não compensar o código mais complexo de hoje, então raramente é feito).


Da mesma forma, string::datapermite uma representação interna diferente, por exemplo, uma corda (lista vinculada de segmentos de corda). Isso pode melhorar significativamente as operações de inserção / substituição. novamente, a lista de segmentos teria que ser reduzida a um único segmento quando você chamar c_strou data.

Peterchen
fonte
2

Citação 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
fonte
2

Todos os comentários anteriores são consistentes, mas eu também gostaria de adicionar que começando em c ++ 17, str.data () retorna um char * em vez de const char *

Nam Vu
fonte
1
Ambos conste non-constsobrecargas estão disponíveis desde C ++ 17.
Gupta de