1) L'utilisation la plus courante de goto que je connaisse est l'émulation de la gestion des exceptions dans les langages qui ne l'offrent pas, à savoir en C. (Le code donné par Nuclear ci-dessus est juste cela.) Regardez le code source Linux et vous ' Je verrai un bazillion de gotos utilisés de cette façon; il y avait environ 100 000 gotos dans le code Linux selon une enquête rapide menée en 2013: http://blog.regehr.org/archives/894 . L'utilisation de Goto est même mentionnée dans le guide de style de codage Linux: https://www.kernel.org/doc/Documentation/CodingStyle . Tout comme la programmation orientée objet est émulée à l'aide de structures peuplées de pointeurs de fonction, goto a sa place dans la programmation C. Alors, qui a raison: Dijkstra ou Linus (et tous les codeurs du noyau Linux)? C'est de la théorie contre la pratique.
Il y a cependant le piège habituel de ne pas avoir de support au niveau du compilateur et de vérifier les constructions / modèles courants: il est plus facile de les mal utiliser et d'introduire des bogues sans vérifications au moment de la compilation. Windows et Visual C ++ mais en mode C offrent une gestion des exceptions via SEH / VEH pour cette raison: les exceptions sont utiles même en dehors des langages OOP, c'est-à-dire dans un langage procédural. Mais le compilateur ne peut pas toujours sauvegarder votre bacon, même s'il offre un support syntaxique pour les exceptions dans le langage. Prenons comme exemple de ce dernier cas le fameux bogue Apple SSL "goto fail", qui vient de dupliquer un goto avec des conséquences désastreuses ( https://www.imperialviolet.org/2014/02/22/applebug.html ):
if (something())
goto fail;
goto fail; // copypasta bug
printf("Never reached\n");
fail:
// control jumps here
Vous pouvez avoir exactement le même bogue en utilisant des exceptions prises en charge par le compilateur, par exemple en C ++:
struct Fail {};
try {
if (something())
throw Fail();
throw Fail(); // copypasta bug
printf("Never reached\n");
}
catch (Fail&) {
// control jumps here
}
Mais les deux variantes du bogue peuvent être évitées si le compilateur analyse et vous avertit du code inaccessible. Par exemple, la compilation avec Visual C ++ au niveau d'avertissement / W4 trouve le bogue dans les deux cas. Java, par exemple, interdit le code inaccessible (où il peut le trouver!) Pour une assez bonne raison: il s'agit probablement d'un bogue dans le code moyen de Joe. Tant que la construction goto ne permet pas aux cibles que le compilateur ne peut pas facilement comprendre, comme les gotos aux adresses calculées (**), il n'est pas plus difficile pour le compilateur de trouver du code inaccessible dans une fonction avec gotos que d'utiliser Dijkstra - code approuvé.
(**) Note de bas de page: Les gotos vers les numéros de ligne calculés sont possibles dans certaines versions de Basic, par exemple GOTO 10 * x où x est une variable. De façon assez confuse, dans Fortran, "goto calculé" fait référence à une construction qui est équivalente à une instruction switch en C. La norme C n'autorise pas les gotos calculés dans le langage, mais seulement les gotos aux étiquettes déclarées statiquement / syntaxiquement. GNU C a cependant une extension pour obtenir l'adresse d'une étiquette (l'opérateur unaire, préfixe &&) et permet également d'accéder à une variable de type void *. Voir https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html pour en savoir plus sur ce sous-sujet obscur. Le reste de ce post ne concerne pas cette obscure fonctionnalité GNU C.
Les gotos C standard (c'est-à-dire non calculés) ne sont généralement pas la raison pour laquelle le code inaccessible ne peut pas être trouvé au moment de la compilation. La raison habituelle est un code logique comme le suivant. Donné
int computation1() {
return 1;
}
int computation2() {
return computation1();
}
Il est tout aussi difficile pour un compilateur de trouver du code inaccessible dans l'une des 3 constructions suivantes:
void tough1() {
if (computation1() != computation2())
printf("Unreachable\n");
}
void tough2() {
if (computation1() == computation2())
goto out;
printf("Unreachable\n");
out:;
}
struct Out{};
void tough3() {
try {
if (computation1() == computation2())
throw Out();
printf("Unreachable\n");
}
catch (Out&) {
}
}
(Excusez mon style de codage lié à l'accolade, mais j'ai essayé de garder les exemples aussi compacts que possible.)
Visual C ++ / W4 (même avec / Ox) ne parvient pas à trouver de code inaccessible dans aucun d'entre eux, et comme vous le savez probablement, le problème de la recherche de code inaccessible est en général indécidable. (Si vous ne me croyez pas à ce sujet: https://www.cl.cam.ac.uk/teaching/2006/OptComp/slides/lecture02.pdf )
Comme problème connexe, le goto C peut être utilisé pour émuler des exceptions uniquement à l'intérieur du corps d'une fonction. La bibliothèque C standard offre une paire de fonctions setjmp () et longjmp () pour émuler des sorties / exceptions non locales, mais celles-ci présentent de sérieux inconvénients par rapport à ce que d'autres langues offrent. L'article Wikipedia http://en.wikipedia.org/wiki/Setjmp.h explique assez bien ce dernier problème. Cette paire de fonctions fonctionne également sous Windows ( http://msdn.microsoft.com/en-us/library/yz2ez4as.aspx ), mais presque personne ne les utilise là-bas car SEH / VEH est supérieur. Même sous Unix, je pense que setjmp et longjmp sont très rarement utilisés.
2) Je pense que la deuxième utilisation la plus courante de goto en C consiste à implémenter une interruption à plusieurs niveaux ou une poursuite à plusieurs niveaux, ce qui est également un cas d'utilisation assez peu controversé. Rappelez-vous que Java n'autorise pas le goto label, mais autorise break label ou continue label. Selon http://www.oracle.com/technetwork/java/simple-142616.html , il s'agit en fait du cas d'utilisation le plus courant des gotos en C (90% disent-ils), mais selon mon expérience subjective, le code système tend à d'utiliser gotos pour la gestion des erreurs plus souvent. Peut-être dans le code scientifique ou lorsque le système d'exploitation offre une gestion des exceptions (Windows), alors les sorties à plusieurs niveaux sont le cas d'utilisation dominant. Ils ne donnent pas vraiment de détails sur le contexte de leur enquête.
Modifié pour ajouter: il s'avère que ces deux modèles d'utilisation se trouvent dans le livre C de Kernighan et Ritchie, vers la page 60 (selon l'édition). Une autre chose à noter est que les deux cas d'utilisation impliquent uniquement des gotos avancés. Et il s'avère que l'édition MISRA C 2012 (contrairement à l'édition 2004) autorise désormais les gotos, tant qu'ils ne sont que des versions avancées.