95 votes

L'utilisation de goto entraînera-t-elle une fuite des variables ?

Est-il vrai que goto saute d'un bout à l'autre du code sans appeler les destructeurs et autres ?

par exemple

void f() {
   int x = 0;
   goto lol;
}

int main() {
   f();
lol:
   return 0;
}

Ne le fera pas x être divulguée ?

0 votes

En rapport : stackoverflow.com/questions/1258201/ (mais je voulais le faire à partir de zéro, proprement !)

15 votes

Qu'est-ce que "Won't x be leaked" signifie ? Le type de x est un type de données intégré. Pourquoi ne pas choisir un meilleur exemple ?

2 votes

@Nawaz : L'exemple est parfait tel qu'il est. Presque chaque fois que je parle à quelqu'un de goto Ils pensent que même les variables de durée de stockage automatique font l'objet d'une "fuite". Le fait que vous et moi sachions le contraire n'a rien à voir avec la question.

212voto

Lightness Races in Orbit Points 122793

<strong>Avertissement : </strong>Cette réponse concerne C++ <em>seulement </em>; les règles sont très différentes en C.


Ne le fera pas x être divulguée ?

Non, absolument pas.

C'est un mythe que goto est une construction de bas niveau qui permet d'outrepasser les mécanismes de cadrage intégrés au C++. (S'il y a quelque chose, c'est longjmp qui peuvent être sujettes à ce phénomène).

Considérez les mécanismes suivants qui vous empêchent de faire de "mauvaises choses" avec les étiquettes (ce qui inclut case étiquettes).


1. Champ d'application de l'étiquette

Il n'est pas possible de passer d'une fonction à l'autre :

void f() {
   int x = 0;
   goto lol;
}

int main() {
   f();
lol:
   return 0;
}

// error: label 'lol' used but not defined

[n3290: 6.1/1]: [ ] La portée d'une étiquette est la fonction dans laquelle elle apparaît. dans laquelle elle apparaît. [..]


2. Initialisation des objets

Il n'est pas possible de passer outre l'initialisation d'un objet :

int main() {
   goto lol;
   int x = 0;
lol:
   return 0;
}

// error: jump to label ‘lol’
// error:   from here
// error:   crosses initialization of ‘int x’

Si vous sautez retour lors de l'initialisation de l'objet, alors l'instance précédente de l'objet est détruite :

struct T {
   T() { cout << "*T"; }
  ~T() { cout << "~T"; }
};

int main() {
   int x = 0;

  lol:
   T t;
   if (x++ < 5)
     goto lol;
}

// Output: *T~T*T~T*T~T*T~T*T~T*T~T

[n3290: 6.6/2]: [ ] Transférer hors d'une boucle, hors d'un bloc, ou revenir en arrière d'une variable initialisée avec une durée de stockage automatique implique la destruction d'objets ayant une durée de stockage automatique qui sont dans portée au moment du transfert, mais pas au moment du transfert. vers. [..]

Vous ne pouvez pas entrer dans le champ d'application d'un objet, même s'il n'est pas explicitement initialisé :

int main() {
   goto lol;
   {
      std::string x;
lol:
      x = "";
   }
}

// error: jump to label ‘lol’
// error:   from here
// error:   crosses initialization of ‘std::string x’

... à l'exception de certains types d'objets que le langage peut traiter sans problème parce qu'ils ne nécessitent pas de construction "complexe" :

int main() {
   goto lol;
   {
      int x;
lol:
      x = 0;
   }
}

// OK

[n3290: 6.7/3]: Il est possible d'effectuer un transfert dans un bloc, mais pas d'une manière qui permette de contourner la loi. mais pas d'une manière qui contourne les déclarations avec l'initialisation. Un programme qui saute d'un point où une variable à durée de stockage automatique n'est pas dans la portée à un point où elle est dans la portée est mal formé à moins que la variable est de type scalaire, de type classe avec un constructeur par défaut trivial et une et un destructeur trivial, une version qualifiée cv d'un de ces types, ou un de ces types, ou un tableau de l'un des types précédents et est déclarée sans initialisateur. [..]


3. Le saut respecte la portée des autres objets

De même, les objets à durée de stockage automatique son no "fuit" lorsque vous goto hors de leur champ d'application :

struct T {
   T() { cout << "*T"; }
  ~T() { cout << "~T"; }
};

int main() {
   {
      T t;
      goto lol;
   }

lol:
   return 0;
}

// *T~T

[n3290: 6.6/2]: Lors de la sortie d'un champ d'application (quelle que soit la manière dont il a été réalisé), les objets avec une durée de stockage automatique (3.7.3) qui ont été construits dans cette portée sont détruits dans l'ordre inverse de leur construction. [..]


Conclusion

Les mécanismes susmentionnés garantissent que goto ne vous permet pas d'enfreindre la langue.

Bien entendu, cela ne signifie pas automatiquement que vous "devriez" utiliser goto pour un problème donné, mais il fait Cela signifie qu'elle est loin d'être aussi "maléfique" que le mythe commun le laisse entendre.

0 votes

@Ben : Ne le fait pas [n3290: 18.10/4]: "La signature de la fonction longjmp(jmp_buf jbuf, int val) a un comportement plus restreint dans cette norme internationale. A setjmp / longjmp a un comportement indéfini si l'on remplace la paire d'appels setjmp y longjmp par catch et throw invoquerait tout destructeur non trivial pour tout objet automatique." casser cela ?

8 votes

Vous remarquerez que C n'empêche pas toutes ces choses dangereuses de se produire.

13 votes

@Daniel : La question et la réponse concernent très spécifiquement le C++, mais c'est juste. Peut-être pourrions-nous avoir une autre FAQ qui dissiperait le mythe selon lequel C et C++ sont identiques ;)

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