string c_str () vs data ()

102

J'ai lu plusieurs endroits où la différence entre c_str()et data()(dans STL et d'autres implémentations) est qu'il c_str()est toujours terminé par zéro alors que data()ne l'est pas. Pour autant que je l'ai vu dans les implémentations réelles, ils font la même chose ou data()appellent c_str().

Qu'est-ce que j'oublie ici? Lequel est le plus correct à utiliser dans quels scénarios?

Loup
la source

Réponses:

105

La documentation est correcte. À utiliser c_str()si vous voulez une chaîne terminée par null.

Si les implémenteurs arrivent à implémenter data()en termes de que c_str()vous n'avez pas à vous inquiéter, utilisez toujours data()si vous n'avez pas besoin que la chaîne soit terminée par null, dans certaines implémentations, il peut s'avérer plus performant que c_str ().

les chaînes ne doivent pas nécessairement être composées de données de caractère, elles peuvent être composées d'éléments de n'importe quel type. Dans ces cas, data()c'est plus significatif. c_str()à mon avis, ce n'est vraiment utile que lorsque les éléments de votre chaîne sont basés sur des caractères.

Extra : à partir de C ++ 11, les deux fonctions doivent être identiques. c'est-à data- dire qu'il doit maintenant être terminé par zéro. Selon cppreference : "Le tableau retourné est terminé par un null, c'est-à-dire que data () et c_str () exécutent la même fonction."

Scott Langham
la source
4
Extra 2: à partir de C ++ 17, il existe désormais une surcharge .data()non constante pour , donc elles ne sont plus équivalentes pour les chaînes non constantes.
Deduplicator
29

Dans C ++ 11 / C ++ 0x , data()et c_str()n'est plus différent. Et donc il data()est également nécessaire d'avoir une terminaison nulle à la fin.

21.4.7.1 basic_stringaccesseurs [string.accessors]

const charT* c_str() const noexcept;

const charT* data() const noexcept;

1 Renvoie: Un pointeur p tel que p + i == &operator[](i)pour chaque ientrée [0,size()].


21.4.5 Accès à l'élément basic_string [string.access]

const_reference operator[](size_type pos) const noexcept;

1 Nécessite: pos <= size (). 2 Renvoie:, *(begin() + pos) if pos < size()sinon une référence à un objet de type T avec une valeur charT();la valeur référencée ne doit pas être modifiée.

mfazekas
la source
Que faire si la chaîne est composée de données autres que des caractères, ce qui est légal pour les données de chaîne AFAIK, y compris null?
taz
3
@taz Même lors du stockage de données binaires, C ++ 11 nécessite d' std::stringallouer un supplément charpour une fin '\0'. Lorsque vous le faites std::string s("\0");, les deux s.data()[0]et s.data()[1]sont garantis d'évaluer à 0.
bcrist
19

Même si vous avez vu qu'ils font de même, ou que .data () appelle .c_str (), il n'est pas correct de supposer que ce sera le cas pour d'autres compilateurs. Il est également possible que votre compilateur change avec une future version.

2 raisons d'utiliser std :: string:

std :: string peut être utilisé à la fois pour du texte et des données binaires arbitraires.

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

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

Vous devez utiliser la méthode .c_str () lorsque vous utilisez votre chaîne comme exemple 1.

Vous devez utiliser la méthode .data () lorsque vous utilisez votre chaîne comme exemple 2. Non pas parce qu'il est dangereux d'utiliser .c_str () dans ces cas, mais parce qu'il est plus explicite que vous travaillez avec des données binaires pour que d'autres vérifient votre code.

Piège possible avec l'utilisation de .data ()

Le code suivant est incorrect et peut provoquer une erreur de segmentation dans votre programme:

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

Pourquoi est-il courant pour les développeurs de faire faire la même chose à .data () et .c_str ()?

Parce qu'il est plus efficace de le faire. La seule façon de faire en sorte que .data () renvoie quelque chose qui n'est pas terminé par null serait d'avoir .c_str () ou .data () copier leur tampon interne, ou d'utiliser simplement 2 tampons. Avoir un seul tampon terminé par null signifie toujours que vous pouvez toujours utiliser un seul tampon interne lors de l'implémentation de std :: string.

Brian R. Bondy
la source
6
En fait, le but de .data () est qu'il ne doit pas copier le tampon interne. Cela signifie qu'une implémentation n'a pas à gaspiller un caractère sur \ 0 jusqu'à ce qu'elle soit nécessaire. Vous ne voudriez jamais deux tampons: si vous appelez .c_str (), ajoutez un \ 0 au tampon. .data () peut toujours renvoyer ce tampon.
MSalters
2
Tout à fait d'accord, il serait ridicule d'utiliser 2 tampons. Comment savez-vous que c'est pourquoi .data était destiné?
Brian R. Bondy
@ BrianR.Bondy J'ai essayé ce code: .. auto str = string {"Test \ 0String!" }; cout << "DATA:" << str.data () << endl; La sortie est "Test" et non la chaîne entière, qu'est-ce que j'ai fait de mal?
programmeur
La dernière partie est erronée, data et c_str pourraient utiliser le même tampon sans qu'il soit terminé par 0 - c_str pourrait simplement ajouter le 0 au premier appel.
Rappelez
heads up, c ++ 11 a fait de .data () un alias pour .c_str ()
hanshenrik
3

Il a déjà été répondu, quelques notes sur le but: Liberté de mise en œuvre.

std::stringles opérations - par exemple l'itération, la concaténation et la mutation d'élément - n'ont pas besoin du terminateur zéro. Sauf si vous passez lestring à une fonction qui attend une chaîne terminée par zéro, il peut être omis.

Cela permettrait à une implémentation d'avoir des sous-chaînes partager les données de chaîne réelles: string::substr pourrait contenir en interne une référence aux données de chaîne partagées et la plage de début / fin, évitant la copie (et l'allocation supplémentaire) des données de chaîne réelles. L'implémentation reporterait la copie jusqu'à ce que vous appeliez c_str ou modifiez l'une des chaînes. Aucune copie ne serait jamais faite si les strigns impliqués sont simplement lus.

(l'implémentation de copie sur écriture n'est pas très amusante dans les environnements multithreads, et les économies de mémoire / allocation typiques ne valent pas le code plus complexe aujourd'hui, donc c'est rarement fait).


De même, string::datapermet une représentation interne différente, par exemple une corde (liste chaînée de segments de chaîne). Cela peut améliorer considérablement les opérations d'insertion / remplacement. encore une fois, la liste des segments devrait être réduite en un seul segment lorsque vous appelez c_strou data.

Peterchen
la source
2

Citation 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
la source
2

Tous les commentaires précédents sont cohérents, mais j'aimerais également ajouter qu'à partir de c ++ 17, str.data () renvoie un char * au lieu de const char *

Nam Vu
la source
1
Les deux constet les non-constsurcharges sont disponibles depuis C ++ 17.
Gupta