web-dev-qa-db-fra.com

string.c_str () désallocation nécessaire?

Mon code convertit assez souvent les chaînes C++ en chaînes CStrings et je me demande si la chaîne d'origine est allouée sur la pile. La chaîne CString sera-t-elle également allouée sur la pile? Par exemple:

string s = "Hello world";
char* s2 = s.c_str();

s2 sera-t-il alloué sur la pile ou dans le tas? En d'autres termes, devrai-je supprimer s2?

Inversement, si j'ai ce code:

string s = new string("Hello, mr. heap...");
char* s2 = s.c_str();

s2 sera-t-il maintenant sur le tas, comme son origine était sur le tas?

Pour clarifier, quand je demande si s2 est sur le tas, je sais que le pointeur est sur la pile. Je demande si ce qu'il indique sera sur le tas ou la pile.

31
string s = "Hello world";
char* s2 = s.c_str();

Est-ce que s2 sera alloué sur la pile ou dans le tas? En d'autres termes ... Dois-je supprimer s2?

Non, pas delete s2!

s2 est sur la pile si le code ci-dessus est à l'intérieur d'une fonction; Si le code a une portée globale ou d'espace de noms, alors s2 sera dans un segment de données initialisé dynamiquement de manière statique. Quoi qu'il en soit, il s'agit d'un pointeur sur un caractère (qui, dans ce cas, est le premier caractère 'H' de la représentation ASCIIZ du contenu textuel de s). Ce texte lui-même est là où l'objet s a semblé vouloir construire cette représentation. Les implémentations sont autorisées à faire ce qu'elles veulent, mais le choix d'implémentation crucial pour std::string est de savoir s'il fournit une "optimisation de chaîne courte" qui permet aux chaînes très courtes d'être incorporées directement dans l'objet s et si "Hello world" est suffisamment court pour en tirer parti. cette optimisation:

  • si tel est le cas, alors s2 pointera vers la mémoire à l'intérieur de s, qui sera alloué à la pile ou de manière statique comme expliqué pour s2 ci-dessus
  • sinon, dans s, il y aurait un pointeur sur la mémoire allouée dynamiquement (mémoire libre/tas) dans laquelle le contenu "Hello world\0" dont l'adresse est retournée par .c_str() apparaîtrait, et s2 serait une copie de cette valeur de pointeur.

Notez que c_str() est const. Par conséquent, pour que votre code soit compilé, vous devez passer à const char* s2 = ....

Vous ne devez pas delete s2. Les données pour lesquelles s2 est toujours détenu et géré par l'objet s seront invalidées par tout appel aux méthodes non -const de s ou par s qui sortent de la portée.

string s = new string("Hello, mr. heap...");
char* s2 = s.c_str();

S2 sera-t-il maintenant sur le tas, comme son origine était sur le tas?

Ce code ne compile pas car s n'est pas un pointeur et une chaîne n'a pas de constructeur comme string(std::string*). Vous pouvez le changer soit:

string* s = new string("Hello, mr. heap...");

...ou...

string s = *new string("Hello, mr. heap...");

Ce dernier crée une fuite de mémoire et ne sert à rien, supposons le premier. Ensuite:

char* s2 = s.c_str();

... doit devenir ...

char* s2 = s->c_str();

S2 sera-t-il maintenant sur le tas, comme son origine était sur le tas?

Oui. Dans tous les scénarios, en particulier si s est lui-même sur le tas, alors:

  • même s'il existe un tampon d'optimisation de chaîne courte dans s auquel c_str() renvoie un pointeur, il doit être sur le tas, sinon
  • si s utilise un pointeur pour augmenter la mémoire pour stocker le texte, cette mémoire sera également allouée à partir du tas.

Mais encore une fois, même en sachant avec certitude que s2 pointe vers la mémoire allouée au tas, votre code n’a pas besoin de libérer la mémoire. Cette opération sera effectuée automatiquement lorsque la variable s sera supprimée:

string* s = new string("Hello, mr. heap...");
const char* s2 = s->c_str();
...use s2 for something...
delete s;   // "destruct" s and deallocate the heap used for it...

Bien sûr, il vaut généralement mieux utiliser string s("xyz"); sauf si vous avez besoin d'une durée de vie au-delà de la portée locale et d'un std::unique_ptr<std::string> ou std::shared_ptr<std::string> sinon.

36
Tony Delroy

c_str() renvoie un pointeur sur un tampon interne dans l'objet string - vous ne le faites jamais free()/delete.

Il n’est valable que tant que la variable string dans laquelle elle pointe est dans la portée. De plus, si vous appelez une méthode non-constante de l'objet string, sa validité n'est plus garantie. 

http://www.cplusplus.com/reference/string/string/c_str/

(édité pour plus de clarté à partir des commentaires ci-dessous)  

14
Brian Roach

std::string::c_str() renvoie un const char*, pas un char *. C'est une bonne indication que vous n'avez pas besoin de le libérer. La mémoire est gérée par l'instance (voir quelques détails dans ce lien , par exemple), elle n'est donc valide que tant que l'instance de chaîne est valide.

4
vanza

Premièrement, même votre chaîne d'origine n'est pas allouée sur la pile, comme vous semblez le croire. Du moins pas tout à fait. Si votre string s est déclaré en tant que variable locale, seul l'objet string est lui-même "alloué sur la pile". La séquence contrôlée de cet objet chaîne est allouée ailleurs. Vous n'êtes pas censé savoir où il est alloué, mais dans la plupart des cas, il est alloué sur le tas. C'est à dire. la chaîne actuelle "Hello world" stockée par s dans votre premier exemple est généralement allouée sur le tas, quel que soit l'endroit où vous déclarez votre s.

Deuxièmement, à propos de c_str().

Dans la spécification d'origine de C++ (C++ 98), c_str renvoyait généralement un pointeur sur un tampon indépendant alloué quelque part. Encore une fois, vous n'êtes pas censé savoir où il est alloué, mais dans le cas général, il était supposé être alloué sur le tas. La plupart des implémentations de std::string s'assuraient que leur séquence contrôlée était toujours terminée par un zéro, ainsi leur c_str renvoyait un pointeur direct sur la séquence contrôlée.

Dans la nouvelle spécification de C++ (C++ 11), il est maintenant nécessaire que c_str renvoie un pointeur direct sur la séquence contrôlée.

En d'autres termes, dans le cas général, le résultat de c_str pointera vers une mémoire allouée par tas, même pour les objets std::string locaux. Votre premier exemple n'est pas différent de votre deuxième exemple à cet égard. Cependant, dans tous les cas, la mémoire indiquée par c_str() ne vous appartient pas. Vous n'êtes pas censé le désallouer. Vous n'êtes pas censé savoir même où il est attribué.

4
AnT

s2 sera valide tant que s restera dans la portée. C'est un pointeur sur la mémoire que s possède. Voir par exemple cette documentation MSDN : "la chaîne a une durée de vie limitée et appartient à la classe string."

Si vous souhaitez utiliser std::string dans une fonction en tant que fabrique pour la manipulation de chaîne, puis renvoyer des chaînes de style c, vous devez allouer du stockage de segment de mémoire pour la valeur renvoyée. Obtenez de l'espace en utilisant malloc ou new, puis copiez le contenu de s.c_str().

2
japreiss

Est-ce que s2 sera alloué sur la pile ou dans le tas?

Pourrait être dans l'un ou l'autre. Par exemple, si la classe std::string effectue une optimisation de petite chaîne, les données résideront sur la pile si sa taille est inférieure au seuil d'authentification unique, et sur le tas sinon. (Et tout cela en supposant que l'objet std::string lui-même se trouve sur la pile.)

Dois-je supprimer s2?

Non, l'objet tableau de caractères renvoyé par c_str appartient à l'objet chaîne.

S2 sera-t-il maintenant sur le tas, comme son origine était sur le tas?

Dans ce cas, les données résideront probablement dans le tas de toute façon, même en mode SSO. Mais il y a rarement une raison d'allouer dynamiquement un objet std::string.

1

Ça dépend. Si je me souviens bien, CString crée une copie de la chaîne d'entrée. Donc non, vous n'avez pas besoin de routines d'allocation de tas spéciales. 

0
Chris K