Est-ce que l'utilisation de variables de fuite goto?

94

Est-il vrai que goto saute à travers des morceaux de code sans appeler des destructeurs et des choses?

par exemple

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

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

Ne xsera- t-il pas divulgué?

Courses de légèreté en orbite
la source
En relation: stackoverflow.com/questions/1258201/… (mais je voulais le faire à partir de zéro, proprement!)
Courses de légèreté en orbite le
15
Que veut "Won't x be leaked"dire? Le type de xest un type de données intégré. Pourquoi ne choisissez-vous pas un meilleur exemple?
Nawaz
2
@Nawaz: L'exemple est parfait tel qu'il est. Presque chaque fois que j'en parle à quelqu'un goto, ils pensent que même les variables automatiques de durée de stockage sont en quelque sorte «fuites». Que vous et moi savons le contraire est tout à fait autre chose.
Courses de légèreté en orbite le
1
@David: Je suis d'accord que cette question a beaucoup plus de sens lorsque la variable a un destructeur non trivial ... et j'ai regardé dans la réponse de Tomalak et ai trouvé un tel exemple. De plus, même si un intne peut pas fuir, il peut fuir . Par exemple: void f(void) { new int(5); }fuit un fichier int.
Ben Voigt
Pourquoi ne pas changer la question en quelque chose comme "Dans l'exemple donné, le chemin d'exécution du code sera-t-il transféré de f () à main () sans effacer la pile et les autres fonctionnalités de retour de fonction? Est-ce important si un destructeur devait être appelé? Est-ce la même chose en C? " Cela maintiendrait-il l'intention de la question, tout en évitant les éventuelles idées fausses?
Jack V.

Réponses:

210

Avertissement: cette réponse concerne uniquement C ++ ; les règles sont assez différentes en C.


Ne xsera- t-il pas divulgué?

Non, absolument pas.

C'est un mythe qui gotoest une construction de bas niveau qui vous permet de remplacer les mécanismes de portée intégrés de C ++. (Si quelque chose, c'est longjmppeut-être sujet à cela.)

Considérez les mécanismes suivants qui vous empêchent de faire de «mauvaises choses» avec les étiquettes (qui incluent les caseétiquettes).


1. Portée de l'étiquette

Vous ne pouvez pas sauter entre les fonctions:

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. [..]


2. Initialisation d'objet

Vous ne pouvez pas sauter à travers l'initialisation d'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 revenez à 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]:[..] Le transfert hors d'une boucle, hors d'un bloc ou en arrière au-delà d'une variable initialisée avec une durée de stockage automatique implique la destruction d'objets avec une durée de stockage automatique qui sont dans la portée au point transféré mais pas au point transféré vers . [..]

Vous ne pouvez pas sauter dans la portée 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’

... sauf pour certains types d'objets , que le langage peut gérer indépendamment car 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 de transférer dans un bloc, mais pas d'une manière qui contourne les déclarations avec l'initialisation. Un programme qui saute d'un point où une variable avec une durée de stockage automatique n'est pas dans la portée à un point où elle est dans la portée est mal formé sauf si la variable a un type scalaire, un type de classe avec un constructeur par défaut trivial et un destructeur trivial, un cv-qualifié de l'un de ces types, ou un tableau de l'un des types précédents et est déclaré sans initialiseur. [..]


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

De même, les objets avec une durée de stockage automatique ne sont pas «divulgués» lorsque vous gotosortez de leur portée :

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

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

lol:
   return 0;
}

// *T~T

[n3290: 6.6/2]:A la sortie d'un périmètre (quelle qu'en soit la réalisation), les objets à durée de stockage automatique (3.7.3) qui ont été construits dans ce périmètre sont détruits dans l'ordre inverse de leur construction. [..]


Conclusion

Les mécanismes ci-dessus garantissent que gotocela ne vous permet pas de casser la langue.

Bien sûr, cela ne signifie pas automatiquement que vous « devrait » utiliser gotopour un problème donné, mais il ne signifie pas qu'il est loin d'être aussi « mal » , comme les fils de mythe commun les gens à croire.

Courses de légèreté en orbite
la source
8
Vous remarquerez peut-être que C n'empêche pas toutes ces choses dangereuses de se produire.
Daniel
13
@Daniel: La question et la réponse concernent très spécifiquement le C ++, mais c'est juste. Peut-être que nous pouvons avoir une autre FAQ dissipant le mythe selon lequel C et C ++ sont les mêmes;)
Lightness Races in Orbit
3
@Tomalak: Je ne pense pas que nous soyons en désaccord ici. Bon nombre des réponses données sur SO sont explicitement documentées quelque part. Je faisais juste valoir qu'il pourrait être tentant pour un programmeur C de voir cette réponse et de supposer que si cela fonctionne en C ++, cela devrait fonctionner de la même manière en C.
Daniel
2
Vous voudrez peut-être également ajouter que tous ces sauts par-dessus l'initialisation sont les mêmes pour les étiquettes de cas.
PlasmaHH
12
Wow, je venais de supposer que la sémantique de C ++ était cassée pour goto, mais elles sont étonnamment saines! Très bonne réponse.
Joseph Garvin