Qu'est-ce que la durée de vie de std :: string :: c_str ()?

100

Dans l'un de mes programmes, je dois m'interfacer avec du code hérité qui fonctionne avec const char*.

Disons que j'ai une structure qui ressemble à:

struct Foo
{
  const char* server;
  const char* name;
};

Mon application de niveau supérieur ne traite que std::string, j'ai donc pensé à l'utiliser std::string::c_str()pour récupérer des const char*pointeurs.

Mais quelle est la durée de vie c_str()?

Puis-je faire quelque chose comme ça sans faire face à un comportement indéfini?

{
  std::string server = "my_server";
  std::string name = "my_name";

  Foo foo;
  foo.server = server.c_str();
  foo.name = name.c_str();

  // We use foo
  use_foo(foo);

  // Foo is about to be destroyed, before name and server
}

Ou suis-je censé copier immédiatement le résultat de c_str()vers un autre endroit?

Je vous remercie.

ereOn
la source
Cela m'est arrivé lorsque j'ai défini une chaîne locale dans une fonction et que je suis retourné .c_str(). Je ne comprenais pas pourquoi parfois je n'obtiens que des parties de la chaîne, jusqu'à ce que je comprenne que le const char*ne vit pas éternellement, mais jusqu'à ce que la chaîne soit détruite
SomethingSomething

Réponses:

85

Le c_str()résultat devient invalide si le std::stringest détruit ou si une fonction membre non-const de la chaîne est appelée. Donc, généralement, vous voudrez en faire une copie si vous avez besoin de le conserver.

Dans le cas de votre exemple, il semble que les résultats de c_str()sont utilisés en toute sécurité, car les chaînes ne sont pas modifiées dans cette portée. (Cependant, nous ne savons pas ce que font use_foo()ou ~Foo()pourraient faire avec ces valeurs; s'ils copient les chaînes ailleurs, alors ils devraient faire une copie conforme , et pas seulement copier les charpointeurs.)

Kristopher Johnson
la source
Le pointeur c_str () peut être invalide si l'objet std :: string est un objet automatique hors de portée ou en appel à une fonction de création de thread.
GuruM
Pouvez-vous expliquer non-const member function of the string is called.?
Mathew Kurian
2
Une "fonction membre non-const" est une fonction membre qui n'est pas marquée avec le constmot - clé. Une telle fonction peut faire muter le contenu de la chaîne, auquel cas la chaîne peut avoir besoin de réallouer la mémoire pour la version terminée par un nul de la chaîne renvoyée par c_str(). Par exemple, size()et length()sont const, afin que vous puissiez les appeler sans vous soucier de la modification de la chaîne, mais ce clear()n'est pas le cas const.
Kristopher Johnson
23

Techniquement, votre code est correct.

MAIS vous avez écrit de telle manière qu'il est facile de casser pour quelqu'un qui ne connaît pas le code. Pour c_str (), la seule utilisation sûre est lorsque vous le passez en paramètre à une fonction. Sinon, vous vous exposez à des problèmes de maintenance.

Exemple 1:

{
  std::string server = "my_server";
  std::string name   = "my_name";

  Foo foo;
  foo.server = server.c_str();
  foo.name = name.c_str();

  //
  // Imagine this is a long function
  // Now a maintainer can easily come along and see name and server
  // and would never expect that these values need to be maintained as
  // const values so why not re-use them

  name += "Martin";
  // Oops now its broken.

  // We use foo
  use_foo(foo);

  // Foo is about to be destroyed, before name and server
}

Donc, pour la maintenance, rendez-le évident:

Meilleure solution:

{
  // Now they can't be changed.
  std::string const server = "my_server";
  std::string const name   = "my_name";

  Foo foo;
  foo.server = server.c_str();
  foo.name = name.c_str();

  use_foo(foo);    
}

Mais si vous avez des chaînes const, vous n'en avez pas réellement besoin:

{
  char const* server = "my_server";
  char const* name   = "my_name";

  Foo foo;
  foo.server = server;
  foo.name   = name;

  use_foo(foo);
}

D'ACCORD. Pour une raison quelconque, vous les voulez sous forme de chaînes:
pourquoi ne pas les utiliser uniquement dans l'appel:

{
  std::string server = "my_server";
  std::string name = "my_name";

  // guaranteed not to be modified now!!!     
  use_foo(Foo(server.c_str(), name.c_str());
}
Martin York
la source
7

Il est valide jusqu'à ce que l'un des événements suivants se produise à l' stringobjet correspondant :

  • l'objet est détruit
  • l'objet est modifié

Vous êtes d'accord avec votre code à moins que vous ne modifiiez ces stringobjets après la c_str()copie de s foomais avant l' use_foo()appel.

dents acérées
la source
4

La valeur de retour de c_str () n'est valide que jusqu'au prochain appel d'une fonction membre non constante pour la même chaîne

DumbCoder
la source
3

Le const char*retour de c_str()n'est valide que jusqu'au prochain appel non const à l' std::stringobjet. Dans ce cas, tout va bien car vous êtes std::stringtoujours dans la portée de Fooet vous ne faites aucune autre opération qui changerait la chaîne lors de l'utilisation de foo.

AJG85
la source
2

Tant que la chaîne n'est pas détruite ou modifiée, utiliser c_str () est OK. Si la chaîne est modifiée à l'aide d'un c_str () précédemment renvoyé, l'implémentation est définie.

CharlesB
la source
2

Pour être complet, voici une référence et une citation de cppreference.com :

Le pointeur obtenu à partir de c_str()peut être invalidé par:

  • Passer une référence non-const à la chaîne à n'importe quelle fonction de bibliothèque standard, ou
  • Appel de fonctions non membres const sur la string, à l' exclusion operator[], at(), front(), back(), begin(), rbegin(), end()et rend().
Victor Sergienko
la source