2008-10-11 5 views
70

J'ai lu plusieurs endroits que la différence entre c_str() et data() (en STL et d'autres implémentations) est que c_str() est toujours terminée nulle alors que data() ne l'est pas. Pour autant que j'ai vu dans les implémentations réelles, ils font la même chose ou data() appels c_str().chaîne c_str() vs data()

Qu'est-ce qui me manque ici? Lequel est le plus correct à utiliser dans quels scénarios?

Répondre

80

La documentation est correcte. Utilisez c_str() si vous voulez une chaîne terminée par zéro.

Si les happend à mettre en œuvre implémenteurs data() en termes de c_str() vous n'avez pas à vous inquiéter, utilisez toujours data() si vous n'avez pas besoin de la chaîne à nulle fin, dans une mise en œuvre, il peut se révéler de meilleurs résultats que c_str().

Les chaînes ne doivent pas nécessairement être composées de données de caractères, elles peuvent être composées d'éléments de n'importe quel type. Dans ces cas, data() est plus significatif. c_str() à mon avis est seulement vraiment utile 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-à-dire data est maintenant requis pour être terminé par zéro. Selon cppreference: "Le tableau retourné est null-terminé, c'est-à-dire, data() et c_str() effectuent la même fonction."

16

Même sachant que 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 :: chaîne peut être utilisée pour le texte et les 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 car il est dangereux d'utiliser .c_str() dans ces cas, mais parce qu'il est plus explicite que vous utilisiez des données binaires pour d'autres personnes qui examinent votre code.

possible avec l'aide de piège .data()

Le code suivant est mauvais et pourrait 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 commun de faire .data implémenteurs() et .c_str() font la même chose?

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

+6

En fait, le point de .data() est qu'il ne devrait pas copier le tampon interne. Cela signifie qu'une implémentation ne doit pas gaspiller un caractère sur \ 0 tant que cela n'est pas nécessaire. Vous ne voudrez jamais deux tampons: si vous appelez .c_str(), ajoutez un \ 0 au tampon. .data() peut toujours retourner ce tampon. – MSalters

+1

D'accord, il serait ridicule d'utiliser 2 tampons. Comment savez-vous que c'est la raison pour laquelle .data était prévu? –

+0

@ BrianR.Bondy J'ai essayé ce code: ..auto str = string {"Test \ 0String!" } cout << "DATA:" << str.data() << endl; La sortie est "Test" et non toute la chaîne, Qu'ai-je fait de mal? – programmer

1

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

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

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

Cela permettrait une mise en œuvre d'avoir des sous-chaînes partagent les données de chaîne réelle: string::substr pourrait tenir en interne une référence aux données de chaîne partagées, et la plage de début/fin, en évitant la copie (et allocation supplémentaire) des données de chaîne réelle. L'implémentation différerait la copie jusqu'à ce que vous appeliez c_str ou que vous modifiez l'une des chaînes. Aucune copie ne serait jamais faite si les strings impliqués sont juste lus. L'implémentation de copy-on-write n'est pas très amusante dans les environnements multithread, et les économies typiques de mémoire/allocation ne valent pas le code plus complexe aujourd'hui, donc c'est rarement fait.


De même, string::data permet une autre représentation interne, par exemple une corde (liste chaînée de segments de chaîne). Cela peut améliorer les opérations d'insertion/remplacement de manière significative. encore une fois, la liste des segments devrait être réduite à un seul segment lorsque vous appelez c_str ou data.

20

En C++11/C++0x, data() et c_str() n'est plus différent. Et donc data() est requis pour avoir une terminaison nulle à la fin aussi.

21.4.7.1 basic_string accesseurs [string.accessors]

const charT* c_str() const noexcept;

const charT* data() const noexcept;

1 Retourne un pointeur p de telle sorte que pour chaque p + i == &operator[](i)i dans [0,size()].


21.4.5 accès d'élément de basic_string [string.access]

const_reference operator[](size_type pos) const noexcept;

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

+0

Que faire si la chaîne est composée de données non-caractère, ce qui est légal pour les données de chaîne AFAIK, y compris null? – taz

+3

@taz Même lorsque vous stockez des données binaires, C++ 11 requiert que 'std :: string' alloue un 'char' supplémentaire pour un' \ 0 'final. Quand vous faites 'std :: string s (" \ 0 ");', 's.data() [0]' et 's.data() [1]' sont garantis pour évaluer à 0. – bcrist