37 votes

Légale pour écraser le terminateur nul de std::string ?

En C++11, nous savons que std::string est garantie d'être à la fois contiguë et à terminaison nulle (ou, de façon plus pédante, terminée par charT() qui, dans le cas de char est le caractère nul 0).

Il existe une API en C que je dois utiliser pour remplir une chaîne de caractères par pointeur. Elle écrit la chaîne entière + le terminateur nul. En C++03, j'ai toujours été obligé d'utiliser une balise vector<char> parce que je ne pouvais pas supposer que string était contiguë ou à terminaison nulle. Mais en C++11 (en supposant qu'un fichier basic_string qui est encore douteuse dans certaines bibliothèques standard), je peux le faire.

Ou puis-je le faire ? Quand je fais ça :

std::string str(length);

La chaîne allouera length+1 octets, le dernier étant rempli par le terminateur nul. C'est bien. Mais quand je passe ça à l'API C, il va écrire length+1 des personnages. Ça va écraser le terminateur nul.

Certes, il va écraser le null-terminator avec un caractère nul . Il y a de bonnes chances que cela fonctionne (en effet, je ne peux pas imaginer comment il ne pouvait pas travail).

Mais je ne me soucie pas de ce qui "marche". Je veux savoir, selon la spécification si l'on peut remplacer le caractère nul par un caractère nul ?

25voto

Xeo Points 69818

Malheureusement, il s'agit d'UB, si j'interprète correctement la formulation (de toute façon, ce n'est pas autorisé) :

§21.4.5 [string.access] p2

Les retours : *(begin() + pos) si pos < size() sinon une référence à un objet de type T avec valeur charT() ; la valeur référencée ne doit pas être modifiée .

(Erreur rédactionnelle qui dit T no charT .)

.data() y .c_str() renvoient essentiellement à operator[] ( §21.4.7.1 [string.accessors] p1 ):

Les retours : Un pointeur p de sorte que p + i == &operator[](i) pour chaque i en [0,size()] .

11voto

Mr.C64 Points 11681

Selon la spécification, l'écrasement de la terminaison NUL devrait être comportement indéfini . Donc, la bonne chose à faire serait d'attribuer length+1 les caractères de la chaîne, transmet le tampon de chaîne à l'API C, puis resize() retour à length :

// "+ 1" to make room for the terminating NUL for the C API
std::string str(length + 1);

// Call the C API passing &str[0] to safely write to the string buffer
...

// Resize back to length
str.resize(length);

(FWIW, j'ai essayé l'approche "écraser NUL" sur MSVC10, et cela fonctionne bien).

10voto

T.C. Points 22510

LWG 2475 a rendu cela valide en modifiant la spécification de operator[](size()) (texte inséré en gras) :

Sinon, renvoie une référence à un objet de type charT avec valeur charT() où la modification de l'objet à toute valeur autre que charT() conduit à un comportement indéfini.

5voto

Windows programmer Points 5365

Je suppose que le n3092 n'est plus d'actualité mais c'est ce que j'ai. La section 21.4.5 permet l'accès à un seul élément. Il faut que pos <= size(). Si pos < size() alors vous obtenez l'élément réel, sinon (c'est-à-dire si pos == size()) alors vous obtenez une référence non modifiable.

Je pense qu'en ce qui concerne le langage de programmation, un type d'accès qui pourrait modifier la valeur est considéré comme une modification, même si la nouvelle valeur est la même que l'ancienne.

Est-ce que g++ dispose d'une bibliothèque pédagique à laquelle vous pouvez vous rattacher ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X