web-dev-qa-db-fra.com

La déclaration goto ne peut pas traverser la définition de variable?

Supposons que ces codes compilés dans g++:

#include <stdlib.h>

int main() {
    int a =0;

    goto exit;

    int *b = NULL;

exit:
    return 0;
}

g++ lancera des erreurs:

goto_test.c:10:1: error: jump to label ‘exit’ [-fpermissive]
goto_test.c:6:10: error:   from here [-fpermissive]
goto_test.c:8:10: error:   crosses initialization of ‘int* b’

Il semble que goto ne peut pas croiser la définition du pointeur, mais gcc les compile correctement, rien ne se plaint.

Après avoir corrigé l'erreur, nous devons déclarer tous les pointeurs avant toute instruction goto, c'est-à-dire que vous devez déclarer ces pointeurs même si vous n'en avez pas besoin pour le moment (et enfreindre certains principes).

Quelle est la considération de conception d'origine que g++ interdit l'instruction utile tail-goto?


Mettre à jour:

goto peut croiser la déclaration de variable (tout type de variable, non limité au pointeur), mais sauf ceux qui ont une valeur d'initialisation. Si nous supprimons l'affectation NULL ci-dessus, g++ taisez-vous maintenant. Donc, si vous voulez déclarer des variables entre goto- cross-area, ne pas les initialiser (et encore enfreindre certains principes).

26
coanor

Goto ne peut pas ignorer les initialisations de variables, car les objets respectifs n'existeraient pas après le saut, car la durée de vie de l'objet avec une initialisation non triviale commence lorsque cette initialisation est exécutée:

C++ 11 §3.8/1:

[…] La durée de vie d'un objet de type T commence lorsque:

  • un stockage avec l'alignement et la taille appropriés pour le type T est obtenu, et

  • si l'objet a une initialisation non triviale, son initialisation est terminée.

C++ 11 §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é à moins que la variable ait 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 (8.5).

Puisque l'erreur mentionne [-fpermissive], vous pouvez le transformer en avertissement en spécifiant cet indicateur du compilateur. Cela indique deux choses. Qu'elle était autorisée auparavant (la variable existerait, mais ne serait pas initialisée après le saut) et que les développeurs de gcc pensent que la spécification l'interdit.

Le compilateur vérifie uniquement si la variable doit être initialisée, pas si elle est utilisée, sinon les résultats seraient plutôt incohérents. Mais si vous n'avez plus besoin de la variable, vous pouvez mettre fin à sa vie vous-même, rendant le "tail-goto" viable:

int main() {
    int a =0;
    goto exit;
    {
        int *b = NULL;
    }
exit:
    return 0;
}

est parfaitement valable.

En passant, le fichier porte l'extension .c, ce qui suggère qu'il s'agit de C et non de C++. Si vous le compilez avec gcc au lieu de g++, la version originale devrait être compilée, car C n'a pas cette restriction (il n'a que la restriction pour les tableaux de longueur variable - qui n'existent pas du tout en C++).

32
Jan Hudec

Il existe une solution de contournement facile pour ces types primitifs comme int:

 // ---  original form, subject to cross initialization error.  ---
 // int foo = 0;

 // ---  work-around form: no more cross initialization error.  ---
 int foo;  foo = 0;
3
Robin Hsu