2008-10-06 9 views
3

Les littéraux de chaîne que nous utilisons à l'intérieur fonctionnent-ils comme des variables automatiques? Ou sont-ils alloués en tas que nous devons libérer manuellement? J'ai une situation comme le code ci-dessous dans lequel j'affecte une chaîne littérale à un champ privé de la classe (marqué comme UN dans le code) et le récupère beaucoup plus tard dans mon programme et l'utilise (marqué comme deux). Est-ce que j'attribue une variable de la pile à un champ dans UN? Le code peut-il faire référence à un pointeur qui, dans ce cas, fonctionnait parce que le programme était assez petit? Je l'ai compilé et lancé, ça a bien fonctionné mais j'ai un crash étrange dans mon programme actuel où j'attribue des littéraux de chaînes aux champs de la classe comme ça et je suspecte le cas que j'ai mentionné ci-dessus. Dois-je déclarer la variable "s" comme un tableau de caractères à la place d'un pointeur? Je prévois d'utiliser std :: string, mais je suis juste curieux à ce sujet.Littéraux de chaîne dans les fonctions: variables automatiques ou allouées dans le tas?

Tout pointeur ou aide est, comme toujours, très apprécié :) Merci.

Répondre

8

Les littéraux de chaîne seront placés dans le segment de données ou de texte (code) initialisé de votre fichier binaire par le compilateur, plutôt que dans la mémoire (affectée à l'exécution) ou dans la pile. Vous devriez donc utiliser un pointeur, puisque vous allez référencer le littéral de chaîne que le compilateur a déjà produit pour vous. Notez que modifier ceci (ce qui nécessiterait généralement une modification de la protection de la mémoire) changera toutes les utilisations de ce littéral.

+2

La modification est illégale. C'est vraiment un const. –

+0

+1 à const char *. MemoryLeak :: s devrait être const et il devrait être const char * MemoryLeak :: retrieve() – quamrana

0

Peut-être la cause de l'accident est que vous n'avez pas 0-terminer la chaîne?

+0

Erm, les littéraux de chaîne sont toujours terminés par un caractère nul. –

+0

Cela dépend du compilateur, n'est-ce pas? – Treb

+0

Non, sauf si vous avez affaire à un compilateur d'il y a 30 ans: P –

6

Il est un comportement non défini pour modifier un littéral de chaîne, et est probablement la cause de l'accident dans votre programme (ISO C++: 2.13.4/2). La norme permet une conversion d'un littéral de chaîne à char* pour une rétrocompatibilité avec C et vous ne devriez avoir cette conversion dans votre code que si vous en avez absolument besoin.

Si vous souhaitez traiter la chaîne littérale comme constante, vous pouvez changer le type de votre membre en const char *.

Si votre conception nécessite que s puisse être modifié, alors je recommanderais de changer son type à std::string.

1

Merci Cody et Richard.

J'ai trouvé la cause du bug. C'était parce que je faisais une suppression sur un objet qui était déjà supprimé. Je faisais:

if (obj != NULL) delete obj; 

je l'ai changé:

if (obj != NULL) 
{ 
    delete obj; 
    obj = NULL; 
} 

apprentissage C++ est certainement amusant :)

+0

Oui - c'est amusant! En ce qui concerne le bug que vous avez trouvé, je vous recommande vraiment d'utiliser des pointeurs intelligents. Faites une recherche pour "pointeur intelligent" et "RAII" dans StackOverflow et vous trouverez des informations utiles. Les pointeurs intelligents prennent soin de gérer la durée de vie de l'objet pour vous. –

+0

delete devrait être fait dans les destructeurs. Peut-être dans l'affectation, mais même là-bas copier-à-temp-and-swap est généralement plus sûr (le destructeur de la temp nettoie la vieille valeur permutée) – MSalters

+0

@Richard: Je vais regarder les pointeurs intelligents et RAII. @ onebyone.livejournal.com: Merci pour cela. Je vais supprimer les vérifications NULL avant de supprimer dans mon code. Mais comme Richard l'a souligné, les pointeurs intelligents semblent être un meilleur choix mais pas avant que je l'apprenne :) @MSalters: Je ne suis pas sûr de vous avoir compris. :( – Srikanth

0

Permet de jeter un oeil à vos options.
Il y a aussi quelques choses que vous devez faire:

/* 
    * Should initialize s to NULL or a valid string in constructor */ 
     MemoryLeak() 
     { 
      store(); 
     } 

     void store() 
     { 
      // This does not need to be freed because it is a string literal 
      // generated by the compiler. 
      s = "Storing a string"; // ONE 

      // Note this is allowed for backward compatibility but the string is 
      // really stored as a const char* and thus unmodifiable. If somebody 
      // retrieves this C-String and tries to change any of the contents the 
      // code could potentially crash as this is UNDEFINED Behavior. 

      // The following does need to be free'd. 
      // But given the type of s is char* this is more correct. 
      s = strdup("Storing a string"); 

      // This makes a copy of the string on the heap. 
      // Because you allocated the memory it is modifiable by anybody 
      // retrieving it but you also need to explicitly de-allocate it 
      // with free() 
     } 

Ce que vous faites est d'utiliser C-Strings. Ceux-ci ne doivent pas être confondus avec C++ std :: string. La chaîne std :: string C++ est initialisée automatiquement à la chaîne vide. Toute mémoire allouée est désaffectée correctement.Il peut facilement être retourné comme une version modifiable et non modifiable. Il est également facile à manipuler (, c'est-à-dire). Si vous développez un C-String, vous devez réallouer la mémoire et copier la chaîne dans la nouvelle mémoire, etc. (cela prend beaucoup de temps). Pour gérer l'allocation dynamique de votre objet, je voudrais en savoir plus sur les pointeurs intelligents.
Voir cet article pour plus de détails sur les pointeurs intelligents.
Smart Pointers or who owns you Baby

std::auto_ptr<MemoryLeak> obj(new MemoryLeak()); 

obj->store(); 
std::cout << obj->retrieve() << std::endl; // TWO 

// No need to delete When object goes out of scope it auto deletes the memory. 
Questions connexes